Skip to content

Update Bookmark manager for no longer tracking per database #1017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 20 additions & 81 deletions packages/core/src/bookmark-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,66 +36,40 @@ export default class BookmarkManager {
* Method called when the bookmarks get updated when a transaction finished.
*
* This method will be called when auto-commit queries finish and when explicit transactions
* get commited.
* get committed.
*
* @param {string} database The database which the bookmarks belongs to
* @param {Iterable<string>} previousBookmarks The bookmarks used when starting the transaction
* @param {Iterable<string>} newBookmarks The new bookmarks received at the end of the transaction.
* @returns {void}
*/
async updateBookmarks (database: string, previousBookmarks: Iterable<string>, newBookmarks: Iterable<string>): Promise<void> {
async updateBookmarks (previousBookmarks: Iterable<string>, newBookmarks: Iterable<string>): Promise<void> {
throw new Error('Not implemented')
}

/**
* Method called by the driver to get the bookmarks for one specific database
* Method called by the driver to get the bookmarks.
*
* @param {string} database The database which the bookmarks belong to
* @returns {Iterable<string>} The set of bookmarks
*/
async getBookmarks (database: string): Promise<Iterable<string>> {
throw new Error('Not implemented')
}

/**
* Method called by the driver for getting all the bookmarks.
*
* This method should return all bookmarks for all databases present in the BookmarkManager.
*
* @returns {Iterable<string>} The set of bookmarks
*/
async getAllBookmarks (): Promise<Iterable<string>> {
throw new Error('Not implemented')
}

/**
* Forget the databases and its bookmarks
*
* This method is not called by the driver. Forgetting unused databases is the user's responsibility.
*
* @param {Iterable<string>} databases The databases which the bookmarks will be removed for.
*/
async forget (databases: Iterable<string>): Promise<void> {
async getBookmarks (): Promise<Iterable<string>> {
throw new Error('Not implemented')
}
}

export interface BookmarkManagerConfig {
initialBookmarks?: Map<string, Iterable<string>>
bookmarksSupplier?: (database?: string) => Promise<Iterable<string>>
bookmarksConsumer?: (database: string, bookmarks: Iterable<string>) => Promise<void>
initialBookmarks?: Iterable<string>
bookmarksSupplier?: () => Promise<Iterable<string>>
bookmarksConsumer?: (bookmarks: Iterable<string>) => Promise<void>
}

/**
* @typedef {Object} BookmarkManagerConfig
*
* @since 5.0
* @experimental
* @property {Map<string,Iterable<string>>} [initialBookmarks@experimental] Defines the initial set of bookmarks. The key is the database name and the values are the bookmarks.
* @property {function([database]: string):Promise<Iterable<string>>} [bookmarksSupplier] Called for supplying extra bookmarks to the BookmarkManager
* 1. supplying bookmarks from the given database when the default BookmarkManager's `.getBookmarks(database)` gets called.
* 2. supplying all the bookmarks when the default BookmarkManager's `.getAllBookmarks()` gets called
* @property {function(database: string, bookmarks: Iterable<string>): Promise<void>} [bookmarksConsumer] Called when the set of bookmarks for database get updated
* @property {Iterable<string>} [initialBookmarks] Defines the initial set of bookmarks. The key is the database name and the values are the bookmarks.
* @property {function():Promise<Iterable<string>>} [bookmarksSupplier] Called for supplying extra bookmarks to the BookmarkManager
* @property {function(bookmarks: Iterable<string>): Promise<void>} [bookmarksConsumer] Called when the set of bookmarks get updated
*/
/**
* Provides an configured {@link BookmarkManager} instance.
Expand All @@ -106,9 +80,7 @@ export interface BookmarkManagerConfig {
* @returns {BookmarkManager}
*/
export function bookmarkManager (config: BookmarkManagerConfig = {}): BookmarkManager {
const initialBookmarks = new Map<string, Set<string>>()

config.initialBookmarks?.forEach((v, k) => initialBookmarks.set(k, new Set(v)))
const initialBookmarks = new Set(config.initialBookmarks)

return new Neo4jBookmarkManager(
initialBookmarks,
Expand All @@ -119,69 +91,36 @@ export function bookmarkManager (config: BookmarkManagerConfig = {}): BookmarkMa

class Neo4jBookmarkManager implements BookmarkManager {
constructor (
private readonly _bookmarksPerDb: Map<string, Set<string>>,
private readonly _bookmarksSupplier?: (database?: string) => Promise<Iterable<string>>,
private readonly _bookmarksConsumer?: (database: string, bookmark: Iterable<string>) => Promise<void>
private readonly _bookmarks: Set<string>,
private readonly _bookmarksSupplier?: () => Promise<Iterable<string>>,
private readonly _bookmarksConsumer?: (bookmark: Iterable<string>) => Promise<void>
) {

}

async updateBookmarks (database: string, previousBookmarks: Iterable<string>, newBookmarks: Iterable<string>): Promise<void> {
const bookmarks = this._getOrInitializeBookmarks(database)
async updateBookmarks (previousBookmarks: Iterable<string>, newBookmarks: Iterable<string>): Promise<void> {
const bookmarks = this._bookmarks
for (const bm of previousBookmarks) {
bookmarks.delete(bm)
}
for (const bm of newBookmarks) {
bookmarks.add(bm)
}
if (typeof this._bookmarksConsumer === 'function') {
await this._bookmarksConsumer(database, [...bookmarks])
}
}

private _getOrInitializeBookmarks (database: string): Set<string> {
let maybeBookmarks = this._bookmarksPerDb.get(database)
if (maybeBookmarks === undefined) {
maybeBookmarks = new Set()
this._bookmarksPerDb.set(database, maybeBookmarks)
}
return maybeBookmarks
}

async getBookmarks (database: string): Promise<Iterable<string>> {
const bookmarks = new Set(this._bookmarksPerDb.get(database))

if (typeof this._bookmarksSupplier === 'function') {
const suppliedBookmarks = await this._bookmarksSupplier(database) ?? []
for (const bm of suppliedBookmarks) {
bookmarks.add(bm)
}
await this._bookmarksConsumer([...bookmarks])
}

return [...bookmarks]
}

async getAllBookmarks (): Promise<Iterable<string>> {
const bookmarks = new Set<string>()
async getBookmarks (): Promise<Iterable<string>> {
const bookmarks = new Set(this._bookmarks)

for (const [, dbBookmarks] of this._bookmarksPerDb) {
for (const bm of dbBookmarks) {
bookmarks.add(bm)
}
}
if (typeof this._bookmarksSupplier === 'function') {
const suppliedBookmarks = await this._bookmarksSupplier() ?? []
for (const bm of suppliedBookmarks) {
bookmarks.add(bm)
}
}

return bookmarks
}

async forget (databases: Iterable<string>): Promise<void> {
for (const database of databases) {
this._bookmarksPerDb.delete(database)
}
return [...bookmarks]
}
}
6 changes: 3 additions & 3 deletions packages/core/src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ class SessionConfig {
* Enabling it is done by supplying an BookmarkManager implementation instance to this param.
* A default implementation could be acquired by calling the factory function {@link bookmarkManager}.
*
* **Warning**: Share the same BookmarkManager instance accross all session can have a negative impact
* **Warning**: Sharing the same BookmarkManager instance across multiple sessions can have a negative impact
* on performance since all the queries will wait for the latest changes being propagated across the cluster.
* For keeping consistency between a group of queries, use {@link Session} for grouping them.
* For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for groupping them.
* For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for grouping them.
*
* @example
* const bookmarkManager = neo4j.bookmarkManager()
Expand All @@ -214,7 +214,7 @@ class SessionConfig {
*
* // Reading Driver User will wait of the changes being propagated to the server before RUN the query
* // So the 'Driver User' person should exist in the Result, unless deleted.
* const linkedSesssion2 = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'})
* const linkedResult = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'})
*
* await linkedSession1.close()
* await linkedSession2.close()
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ class Session {
}

private async _bookmarks (): Promise<Bookmarks> {
const bookmarks = await this._bookmarkManager?.getAllBookmarks()
const bookmarks = await this._bookmarkManager?.getBookmarks()
if (bookmarks === undefined) {
return this._lastBookmarks
}
Expand Down Expand Up @@ -489,7 +489,7 @@ class Session {
}

private async _getConnectionAcquistionBookmarks (): Promise<Bookmarks> {
const bookmarks = await this._bookmarkManager?.getBookmarks('system')
const bookmarks = await this._bookmarkManager?.getBookmarks()
if (bookmarks === undefined) {
return this._lastBookmarks
}
Expand All @@ -505,7 +505,6 @@ class Session {
_updateBookmarks (newBookmarks?: Bookmarks, previousBookmarks?: Bookmarks, database?: string): void {
if ((newBookmarks != null) && !newBookmarks.isEmpty()) {
this._bookmarkManager?.updateBookmarks(
database ?? this._database,
previousBookmarks?.values() ?? [],
newBookmarks?.values() ?? []
)
Expand Down
Loading