From 84cf1f72f85990725a02cdeb13fd2e4ddd5db41f Mon Sep 17 00:00:00 2001 From: Luca Pirolo <66700259+lucapirolo@users.noreply.github.com> Date: Wed, 22 Nov 2023 23:06:14 +0000 Subject: [PATCH 1/5] Refactor: Move Bookmarks into Dedicated Package - Relocated all bookmarks-related code to a new 'bookmarks' package. - Updated references from 'neo4j.Bookmarks' to 'bm.Bookmarks' for clarity and to avoid conflicts. - Adopted 'bm' as a package alias to resolve naming conflicts in functions, like in 'cleanupBookmarks(bookmarks bookmarks.Bookmarks)', to prevent ambiguity. - Added Bookmarks aliases in the neo4j package to maintain backward compatibility. - Renamed 'bookmarkManager' to 'DefaultBookmarkManager' for clearer representation and to avoid naming conflicts. --- neo4j/{ => bookmarks}/bookmarks.go | 13 +++--- neo4j/{ => bookmarks}/bookmarks_test.go | 61 +++++++++++++------------ neo4j/config.go | 9 +++- neo4j/driver_test.go | 3 +- neo4j/driver_with_context.go | 22 +++++---- neo4j/driver_with_context_test.go | 16 ++++--- neo4j/session.go | 8 ++-- neo4j/session_bookmarks.go | 20 ++++---- neo4j/session_bookmarks_test.go | 8 ++-- neo4j/session_with_context.go | 20 ++++---- neo4j/session_with_context_test.go | 28 ++++++------ neo4j/test-integration/bookmark_test.go | 41 +++++++++-------- neo4j/test-integration/examples_test.go | 14 +++--- neo4j/test-integration/summary_test.go | 14 +++--- neo4j/transaction_helpers_test.go | 6 ++- test-stress/testcontext.go | 9 ++-- testkit-backend/backend.go | 40 ++++++++-------- 17 files changed, 183 insertions(+), 149 deletions(-) rename neo4j/{ => bookmarks}/bookmarks.go (94%) rename neo4j/{ => bookmarks}/bookmarks_test.go (73%) diff --git a/neo4j/bookmarks.go b/neo4j/bookmarks/bookmarks.go similarity index 94% rename from neo4j/bookmarks.go rename to neo4j/bookmarks/bookmarks.go index f5dbe7c2..65b56baa 100644 --- a/neo4j/bookmarks.go +++ b/neo4j/bookmarks/bookmarks.go @@ -17,12 +17,13 @@ * limitations under the License. */ -package neo4j +package bookmarks import ( "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/collections" "sync" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/collections" ) // Bookmarks is a holder for server-side bookmarks which are used for causally-chained sessions. @@ -56,14 +57,14 @@ type BookmarkManagerConfig struct { BookmarkConsumer func(ctx context.Context, bookmarks Bookmarks) error } -type bookmarkManager struct { +type DefaultBookmarkManager struct { bookmarks collections.Set[string] supplyBookmarks func(context.Context) (Bookmarks, error) consumeBookmarks func(context.Context, Bookmarks) error mutex sync.RWMutex } -func (b *bookmarkManager) UpdateBookmarks(ctx context.Context, previousBookmarks, newBookmarks Bookmarks) error { +func (b *DefaultBookmarkManager) UpdateBookmarks(ctx context.Context, previousBookmarks, newBookmarks Bookmarks) error { if len(newBookmarks) == 0 { return nil } @@ -79,7 +80,7 @@ func (b *bookmarkManager) UpdateBookmarks(ctx context.Context, previousBookmarks return nil } -func (b *bookmarkManager) GetBookmarks(ctx context.Context) (Bookmarks, error) { +func (b *DefaultBookmarkManager) GetBookmarks(ctx context.Context) (Bookmarks, error) { var extraBookmarks Bookmarks if b.supplyBookmarks != nil { bookmarks, err := b.supplyBookmarks(ctx) @@ -102,7 +103,7 @@ func (b *bookmarkManager) GetBookmarks(ctx context.Context) (Bookmarks, error) { } func NewBookmarkManager(config BookmarkManagerConfig) BookmarkManager { - return &bookmarkManager{ + return &DefaultBookmarkManager{ bookmarks: collections.NewSet(config.InitialBookmarks), supplyBookmarks: config.BookmarkSupplier, consumeBookmarks: config.BookmarkConsumer, diff --git a/neo4j/bookmarks_test.go b/neo4j/bookmarks/bookmarks_test.go similarity index 73% rename from neo4j/bookmarks_test.go rename to neo4j/bookmarks/bookmarks_test.go index 2109c3b3..0897d34a 100644 --- a/neo4j/bookmarks_test.go +++ b/neo4j/bookmarks/bookmarks_test.go @@ -1,16 +1,17 @@ -package neo4j_test +package bookmarks_test import ( "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" - . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" "testing" "testing/quick" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) func TestCombineBookmarks(t *testing.T) { - f := func(slices []neo4j.Bookmarks) bool { - concatenation := neo4j.CombineBookmarks(slices...) + f := func(slices []bm.Bookmarks) bool { + concatenation := bm.CombineBookmarks(slices...) totalLen := 0 for _, s := range slices { totalLen += len(s) @@ -40,8 +41,8 @@ func TestBookmarkManager(outer *testing.T) { outer.Parallel() outer.Run("deduplicates initial bookmarks", func(t *testing.T) { - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a", "a", "b"}, + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a", "a", "b"}, }) bookmarks, err := bookmarkManager.GetBookmarks(ctx) @@ -51,7 +52,7 @@ func TestBookmarkManager(outer *testing.T) { }) outer.Run("gets no bookmarks by default", func(t *testing.T) { - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{}) + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{}) getBookmarks := func(db string) bool { bookmarks, err := bookmarkManager.GetBookmarks(ctx) AssertNoError(t, err) @@ -64,11 +65,11 @@ func TestBookmarkManager(outer *testing.T) { }) outer.Run("gets bookmarks along with user-supplied bookmarks", func(t *testing.T) { - expectedBookmarks := neo4j.Bookmarks{"a", "b", "c"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a", "b"}, - BookmarkSupplier: func(context.Context) (neo4j.Bookmarks, error) { - return neo4j.Bookmarks{"b", "c"}, nil + expectedBookmarks := bm.Bookmarks{"a", "b", "c"} + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a", "b"}, + BookmarkSupplier: func(context.Context) (bm.Bookmarks, error) { + return bm.Bookmarks{"b", "c"}, nil }, }) @@ -80,14 +81,14 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("user-supplied bookmarks do not alter internal bookmarks", func(t *testing.T) { calls := 0 expectedBookmarks := []string{"a"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a"}, - BookmarkSupplier: func(ctx2 context.Context) (neo4j.Bookmarks, error) { + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a"}, + BookmarkSupplier: func(ctx2 context.Context) (bm.Bookmarks, error) { defer func() { calls++ }() if calls == 0 { - return neo4j.Bookmarks{"b"}, nil + return bm.Bookmarks{"b"}, nil } return nil, nil }, @@ -102,8 +103,8 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("returned bookmarks are copies", func(t *testing.T) { expectedBookmarks := []string{"a"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a"}, + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a"}, }) bookmarks, err := bookmarkManager.GetBookmarks(ctx) AssertNoError(t, err) @@ -116,8 +117,8 @@ func TestBookmarkManager(outer *testing.T) { }) outer.Run("updates bookmarks", func(t *testing.T) { - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a", "b", "c"}, + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a", "b", "c"}, }) err := bookmarkManager.UpdateBookmarks(ctx, []string{"b", "c"}, []string{"d", "a"}) @@ -132,8 +133,8 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("notifies updated bookmarks for new DB", func(t *testing.T) { notifyHookCalled := false expectedBookmarks := []string{"a", "d"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - BookmarkConsumer: func(_ context.Context, bookmarks neo4j.Bookmarks) error { + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + BookmarkConsumer: func(_ context.Context, bookmarks bm.Bookmarks) error { notifyHookCalled = true AssertEqualsInAnyOrder(t, bookmarks, expectedBookmarks) return nil @@ -153,9 +154,9 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("does not notify updated bookmarks when empty", func(t *testing.T) { initialBookmarks := []string{"a", "b"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ InitialBookmarks: initialBookmarks, - BookmarkConsumer: func(_ context.Context, bookmarks neo4j.Bookmarks) error { + BookmarkConsumer: func(_ context.Context, bookmarks bm.Bookmarks) error { t.Error("I must not be called") return nil }, @@ -172,9 +173,9 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("notifies updated bookmarks for existing DB without bookmarks", func(t *testing.T) { notifyHookCalled := false expectedBookmarks := []string{"a", "d"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ InitialBookmarks: nil, - BookmarkConsumer: func(_ context.Context, bookmarks neo4j.Bookmarks) error { + BookmarkConsumer: func(_ context.Context, bookmarks bm.Bookmarks) error { notifyHookCalled = true AssertEqualsInAnyOrder(t, bookmarks, expectedBookmarks) return nil @@ -195,9 +196,9 @@ func TestBookmarkManager(outer *testing.T) { outer.Run("notifies updated bookmarks for existing DB with previous bookmarks", func(t *testing.T) { notifyHookCalled := false expectedBookmarks := []string{"a", "d"} - bookmarkManager := neo4j.NewBookmarkManager(neo4j.BookmarkManagerConfig{ - InitialBookmarks: neo4j.Bookmarks{"a", "b", "c"}, - BookmarkConsumer: func(_ context.Context, bookmarks neo4j.Bookmarks) error { + bookmarkManager := bm.NewBookmarkManager(bm.BookmarkManagerConfig{ + InitialBookmarks: bm.Bookmarks{"a", "b", "c"}, + BookmarkConsumer: func(_ context.Context, bookmarks bm.Bookmarks) error { notifyHookCalled = true AssertEqualsInAnyOrder(t, bookmarks, expectedBookmarks) return nil diff --git a/neo4j/config.go b/neo4j/config.go index 298c66e4..e4908e86 100644 --- a/neo4j/config.go +++ b/neo4j/config.go @@ -20,16 +20,21 @@ package neo4j import ( - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" "math" "net/url" "time" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" ) // Deprecated: please use config.Config directly. This alias will be removed in 6.0. type Config = config.Config +// Deprecated: please use bookmarks.Bookmarks directly. This alias will be removed in 6.0. +type Bookmarks = bookmarks.Bookmarks + // Deprecated: please use config.ServerAddressResolver directly. This alias will be removed in 6.0. type ServerAddressResolver = config.ServerAddressResolver diff --git a/neo4j/driver_test.go b/neo4j/driver_test.go index 27d7547b..bfcc1673 100644 --- a/neo4j/driver_test.go +++ b/neo4j/driver_test.go @@ -23,6 +23,7 @@ import ( "reflect" "testing" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/router" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) @@ -216,7 +217,7 @@ func TestDriverSessionCreation(t *testing.T) { name string testing string mode AccessMode - bookmarks Bookmarks + bookmarks bm.Bookmarks }{ {"Write", "bolt://localhost:7687", AccessModeWrite, []string(nil)}, {"Read", "bolt://localhost:7687", AccessModeRead, []string(nil)}, diff --git a/neo4j/driver_with_context.go b/neo4j/driver_with_context.go index 7e0b2e05..8fec1832 100644 --- a/neo4j/driver_with_context.go +++ b/neo4j/driver_with_context.go @@ -23,15 +23,17 @@ package neo4j import ( "context" "fmt" + "net/url" + "strings" + "sync" + "time" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/auth" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/racing" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" - "net/url" - "strings" - "sync" - "time" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/connector" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/pool" @@ -63,7 +65,7 @@ type DriverWithContext interface { // // maintain consistency with sessions as well // session := driver.NewSession(ctx, neo4j.SessionConfig {BookmarkManager: bookmarkManager}) // // [...] run something within the session - ExecuteQueryBookmarkManager() BookmarkManager + ExecuteQueryBookmarkManager() bm.BookmarkManager // Target returns the url this driver is bootstrapped Target() url.URL // NewSession creates a new session based on the specified session configuration. @@ -324,7 +326,7 @@ type driverWithContext struct { executeQueryBookmarkManagerInitializer sync.Once // instance of the bookmark manager only used by default by managed sessions of ExecuteQuery // this is *not* used by default by user-created session (see NewSession) - executeQueryBookmarkManager BookmarkManager + executeQueryBookmarkManager bm.BookmarkManager auth auth.TokenManager now func() time.Time } @@ -544,10 +546,10 @@ func ExecuteQuery[T any]( return result.(T), err } -func (d *driverWithContext) ExecuteQueryBookmarkManager() BookmarkManager { +func (d *driverWithContext) ExecuteQueryBookmarkManager() bm.BookmarkManager { d.executeQueryBookmarkManagerInitializer.Do(func() { if d.executeQueryBookmarkManager == nil { // this allows tests to init the field themselves - d.executeQueryBookmarkManager = NewBookmarkManager(BookmarkManagerConfig{}) + d.executeQueryBookmarkManager = bm.NewBookmarkManager(bm.BookmarkManagerConfig{}) } }) return d.executeQueryBookmarkManager @@ -642,7 +644,7 @@ func ExecuteQueryWithDatabase(db string) ExecuteQueryConfigurationOption { } // ExecuteQueryWithBookmarkManager configures DriverWithContext.ExecuteQuery to rely on the specified BookmarkManager -func ExecuteQueryWithBookmarkManager(bookmarkManager BookmarkManager) ExecuteQueryConfigurationOption { +func ExecuteQueryWithBookmarkManager(bookmarkManager bm.BookmarkManager) ExecuteQueryConfigurationOption { return func(configuration *ExecuteQueryConfiguration) { configuration.BookmarkManager = bookmarkManager } @@ -667,7 +669,7 @@ type ExecuteQueryConfiguration struct { Routing RoutingControl ImpersonatedUser string Database string - BookmarkManager BookmarkManager + BookmarkManager bm.BookmarkManager BoltLogger log.BoltLogger } diff --git a/neo4j/driver_with_context_test.go b/neo4j/driver_with_context_test.go index c79f0bee..a013c6aa 100644 --- a/neo4j/driver_with_context_test.go +++ b/neo4j/driver_with_context_test.go @@ -23,14 +23,16 @@ import ( "context" "errors" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/racing" - . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" "net/url" "sync" "sync/atomic" "testing" "time" "unsafe" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/racing" + . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) func TestDriverExecuteQuery(outer *testing.T) { @@ -450,7 +452,7 @@ func TestDriverExecuteQuery(outer *testing.T) { callExecuteQueryOrBookmarkManagerGetter(driver, i) storeBookmarkManagerAddress( &bookmarkManagerAddresses, - driver.delegate.executeQueryBookmarkManager.(*bookmarkManager)) + driver.delegate.executeQueryBookmarkManager.(*bm.DefaultBookmarkManager)) wait.Done() }(i) } @@ -464,7 +466,7 @@ func TestDriverExecuteQuery(outer *testing.T) { if len(addressCounts) != 1 { t.Errorf("expected exactly 1 bookmark manager pointer to have been created, got %v", addressCounts) } - address := uintptr(unsafe.Pointer(driver.delegate.executeQueryBookmarkManager.(*bookmarkManager))) + address := uintptr(unsafe.Pointer(driver.delegate.executeQueryBookmarkManager.(*bm.DefaultBookmarkManager))) if count, found := addressCounts[address]; !found || count != int32(goroutineCount) { t.Errorf("expected pointer address %v to be seen %d time(s), got these instead %v", address, count, addressCounts) } @@ -481,7 +483,7 @@ func callExecuteQueryOrBookmarkManagerGetter(driver DriverWithContext, i int) { } } -func storeBookmarkManagerAddress(bookmarkManagerAddresses *sync.Map, bookmarkMgr *bookmarkManager) { +func storeBookmarkManagerAddress(bookmarkManagerAddresses *sync.Map, bookmarkMgr *bm.DefaultBookmarkManager) { address := uintptr(unsafe.Pointer(bookmarkMgr)) defaultCount := int32(1) if count, loaded := bookmarkManagerAddresses.LoadOrStore(address, &defaultCount); loaded { @@ -519,7 +521,7 @@ type driverDelegate struct { newSession func(context.Context, SessionConfig) SessionWithContext } -func (d *driverDelegate) ExecuteQueryBookmarkManager() BookmarkManager { +func (d *driverDelegate) ExecuteQueryBookmarkManager() bm.BookmarkManager { return d.delegate.ExecuteQueryBookmarkManager() } @@ -562,7 +564,7 @@ type fakeSession struct { closeErr error } -func (s *fakeSession) LastBookmarks() Bookmarks { +func (s *fakeSession) LastBookmarks() bm.Bookmarks { panic("implement me") } diff --git a/neo4j/session.go b/neo4j/session.go index 4a194637..024a2c3f 100644 --- a/neo4j/session.go +++ b/neo4j/session.go @@ -21,6 +21,8 @@ package neo4j import ( "context" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" ) // Session represents a logical connection (which is not tied to a physical connection) @@ -34,7 +36,7 @@ type Session interface { // LastBookmarks returns the bookmark received following the last successfully completed transaction. // If no bookmark was received or if this transaction was rolled back, the initial set of bookmarks will be // returned. - LastBookmarks() Bookmarks + LastBookmarks() bm.Bookmarks // LastBookmark returns the bookmark received following the last successfully completed transaction. // If no bookmark was received or if this transaction was rolled back, the bookmark value will not be changed. // Warning: this method can lead to unexpected behaviour if the session has not yet successfully completed a @@ -60,7 +62,7 @@ type session struct { delegate *sessionWithContext } -func (s *session) LastBookmarks() Bookmarks { +func (s *session) LastBookmarks() bm.Bookmarks { return s.delegate.LastBookmarks() } @@ -124,7 +126,7 @@ func (s *erroredSession) LastBookmark() string { return "" } -func (s *erroredSession) LastBookmarks() Bookmarks { +func (s *erroredSession) LastBookmarks() bm.Bookmarks { return []string{} } diff --git a/neo4j/session_bookmarks.go b/neo4j/session_bookmarks.go index b8d5ed0c..326b8023 100644 --- a/neo4j/session_bookmarks.go +++ b/neo4j/session_bookmarks.go @@ -19,21 +19,25 @@ package neo4j -import "context" +import ( + "context" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" +) type sessionBookmarks struct { - bookmarkManager BookmarkManager - bookmarks Bookmarks + bookmarkManager bm.BookmarkManager + bookmarks bm.Bookmarks } -func newSessionBookmarks(bookmarkManager BookmarkManager, bookmarks Bookmarks) *sessionBookmarks { +func newSessionBookmarks(bookmarkManager bm.BookmarkManager, bookmarks bm.Bookmarks) *sessionBookmarks { return &sessionBookmarks{ bookmarkManager: bookmarkManager, bookmarks: cleanupBookmarks(bookmarks), } } -func (sb *sessionBookmarks) currentBookmarks() Bookmarks { +func (sb *sessionBookmarks) currentBookmarks() bm.Bookmarks { return sb.bookmarks } @@ -66,7 +70,7 @@ func (sb *sessionBookmarks) replaceSessionBookmarks(newBookmark string) { sb.bookmarks = []string{newBookmark} } -func (sb *sessionBookmarks) getBookmarks(ctx context.Context) (Bookmarks, error) { +func (sb *sessionBookmarks) getBookmarks(ctx context.Context) (bm.Bookmarks, error) { if sb.bookmarkManager == nil { return nil, nil } @@ -75,7 +79,7 @@ func (sb *sessionBookmarks) getBookmarks(ctx context.Context) (Bookmarks, error) // Remove empty string bookmarks to check for "bad" callers // To avoid allocating, first check if this is a problem -func cleanupBookmarks(bookmarks Bookmarks) Bookmarks { +func cleanupBookmarks(bookmarks bm.Bookmarks) bm.Bookmarks { hasBad := false for _, b := range bookmarks { if len(b) == 0 { @@ -88,7 +92,7 @@ func cleanupBookmarks(bookmarks Bookmarks) Bookmarks { return bookmarks } - cleaned := make(Bookmarks, 0, len(bookmarks)-1) + cleaned := make(bm.Bookmarks, 0, len(bookmarks)-1) for _, b := range bookmarks { if len(b) > 0 { cleaned = append(cleaned, b) diff --git a/neo4j/session_bookmarks_test.go b/neo4j/session_bookmarks_test.go index 76b6b9ba..a93d281b 100644 --- a/neo4j/session_bookmarks_test.go +++ b/neo4j/session_bookmarks_test.go @@ -23,6 +23,8 @@ import ( "context" "reflect" "testing" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" ) func TestSessionBookmarks(outer *testing.T) { @@ -132,7 +134,7 @@ type fakeBookmarkManager struct { recordedCalls []invocation } -func (f *fakeBookmarkManager) UpdateBookmarks(ctx context.Context, previousBookmarks, newBookmarks Bookmarks) error { +func (f *fakeBookmarkManager) UpdateBookmarks(ctx context.Context, previousBookmarks, newBookmarks bm.Bookmarks) error { f.recordedCalls = append(f.recordedCalls, invocation{ function: "UpdateBookmarks", arguments: []any{ctx, previousBookmarks, newBookmarks}, @@ -140,7 +142,7 @@ func (f *fakeBookmarkManager) UpdateBookmarks(ctx context.Context, previousBookm return nil } -func (f *fakeBookmarkManager) GetBookmarks(ctx context.Context) (Bookmarks, error) { +func (f *fakeBookmarkManager) GetBookmarks(ctx context.Context) (bm.Bookmarks, error) { f.recordedCalls = append(f.recordedCalls, invocation{ function: "GetBookmarks", arguments: []any{ctx}, @@ -148,7 +150,7 @@ func (f *fakeBookmarkManager) GetBookmarks(ctx context.Context) (Bookmarks, erro return nil, nil } -func (f *fakeBookmarkManager) GetAllBookmarks(ctx context.Context) (Bookmarks, error) { +func (f *fakeBookmarkManager) GetAllBookmarks(ctx context.Context) (bm.Bookmarks, error) { f.recordedCalls = append(f.recordedCalls, invocation{ function: "GetAllBookmarks", arguments: []any{ctx}, diff --git a/neo4j/session_with_context.go b/neo4j/session_with_context.go index 67c123c8..d2ce83ec 100644 --- a/neo4j/session_with_context.go +++ b/neo4j/session_with_context.go @@ -22,14 +22,16 @@ package neo4j import ( "context" "fmt" + "math" + "time" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/collections" idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/pool" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/telemetry" "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" - "math" - "time" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/retry" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" @@ -53,7 +55,7 @@ type SessionWithContext interface { // LastBookmarks returns the bookmark received following the last successfully completed transaction. // If no bookmark was received or if this transaction was rolled back, the initial set of bookmarks will be // returned. - LastBookmarks() Bookmarks + LastBookmarks() bm.Bookmarks lastBookmark() string // BeginTransaction starts a new explicit transaction on this session // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. @@ -90,7 +92,7 @@ type SessionConfig struct { // on the session the bookmark can be retrieved with Session.LastBookmark. All commands executing // within the same session will automatically use the bookmark from the previous command in the // session. - Bookmarks Bookmarks + Bookmarks bm.Bookmarks // DatabaseName sets the target database name for the queries executed within the session created with this // configuration. // Usage of Cypher clauses like USE is not a replacement for this option. @@ -155,7 +157,7 @@ type SessionConfig struct { // and be notified of bookmark updates per database // Since 5.0 // default: nil (no-op) - BookmarkManager BookmarkManager + BookmarkManager bm.BookmarkManager // NotificationsMinSeverity defines the minimum severity level of notifications the server should send. // By default, the driver's settings are used. // Else, this option overrides the driver's settings. @@ -265,7 +267,7 @@ func (s *sessionWithContext) lastBookmark() string { return s.bookmarks.lastBookmark() } -func (s *sessionWithContext) LastBookmarks() Bookmarks { +func (s *sessionWithContext) LastBookmarks() bm.Bookmarks { // Pick up bookmark from pending auto-commit if there is a bookmark on it // Note: the bookmark manager should not be notified here because: // - the results of the autocommit transaction may have not been consumed @@ -573,7 +575,7 @@ func (s *sessionWithContext) getConnection(ctx context.Context, mode idb.AccessM return conn, nil } -func (s *sessionWithContext) retrieveBookmarks(ctx context.Context, conn idb.Connection, sentBookmarks Bookmarks) error { +func (s *sessionWithContext) retrieveBookmarks(ctx context.Context, conn idb.Connection, sentBookmarks bm.Bookmarks) error { if conn == nil { return nil } @@ -762,7 +764,7 @@ func (s *sessionWithContext) resolveHomeDatabase(ctx context.Context) error { return nil } -func (s *sessionWithContext) getBookmarks(ctx context.Context) (Bookmarks, error) { +func (s *sessionWithContext) getBookmarks(ctx context.Context) (bm.Bookmarks, error) { bookmarks, err := s.bookmarks.getBookmarks(ctx) if err != nil { return nil, err @@ -776,7 +778,7 @@ type erroredSessionWithContext struct { err error } -func (s *erroredSessionWithContext) LastBookmarks() Bookmarks { +func (s *erroredSessionWithContext) LastBookmarks() bm.Bookmarks { return nil } diff --git a/neo4j/session_with_context_test.go b/neo4j/session_with_context_test.go index 451b7d57..a78d75bc 100644 --- a/neo4j/session_with_context_test.go +++ b/neo4j/session_with_context_test.go @@ -23,14 +23,16 @@ import ( "context" "errors" "fmt" - idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" "io" "reflect" "sync" "testing" "time" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/db" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" @@ -69,7 +71,7 @@ func TestSession(outer *testing.T) { return &router, &pool, sess } - createSessionWithBookmarks := func(bookmarks Bookmarks) (*RouterFake, *PoolFake, *sessionWithContext) { + createSessionWithBookmarks := func(bookmarks bm.Bookmarks) (*RouterFake, *PoolFake, *sessionWithContext) { sessConfig := SessionConfig{AccessMode: AccessModeRead, Bookmarks: bookmarks, BoltLogger: boltLogger} return createSessionFromConfig(sessConfig) } @@ -199,13 +201,13 @@ func TestSession(outer *testing.T) { outer.Run("Bookmarking", func(inner *testing.T) { inner.Run("Initial bookmarks are returned from LastBookmarks", func(t *testing.T) { - _, _, sess := createSessionWithBookmarks(BookmarksFromRawValues("b1", "b2")) - AssertDeepEquals(t, sess.LastBookmarks(), BookmarksFromRawValues("b1", "b2")) + _, _, sess := createSessionWithBookmarks(bm.BookmarksFromRawValues("b1", "b2")) + AssertDeepEquals(t, sess.LastBookmarks(), bm.BookmarksFromRawValues("b1", "b2")) }) inner.Run("Initial bookmarks are used and cleaned up before usage", func(t *testing.T) { - dirtyBookmarks := BookmarksFromRawValues("", "b1", "", "b2", "") - cleanBookmarks := BookmarksFromRawValues("b1", "b2") + dirtyBookmarks := bm.BookmarksFromRawValues("", "b1", "", "b2", "") + cleanBookmarks := bm.BookmarksFromRawValues("b1", "b2") _, pool, sess := createSessionWithBookmarks(dirtyBookmarks) err := errors.New("make all fail") conn := &ConnFake{Alive: true, RunErr: err, TxBeginErr: err} @@ -256,13 +258,13 @@ func TestSession(outer *testing.T) { // Should call Buffer on connection to ensure that first Run is buffered and // it's bookmark retrieved sess.Run(context.Background(), "cypher", nil) - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{"buffer-1"}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{"buffer-1"}) result, _ := sess.Run(context.Background(), "cypher", nil) - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{"buffer-2"}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{"buffer-2"}) // And finally consuming the last result should give a new bookmark AssertIntEqual(t, consumeCalls, 0) result.Consume(context.Background()) - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{"consume-1"}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{"consume-1"}) }) inner.Run("Pending and invoke tx function", func(t *testing.T) { @@ -287,7 +289,7 @@ func TestSession(outer *testing.T) { if !reflect.DeepEqual([]string{"1"}, rtx.Bookmarks) { t.Errorf("Using unclean or no bookmarks: %+v", rtx) } - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{"1"}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{"1"}) AssertIntEqual(t, bufferCalls, 1) }) @@ -311,7 +313,7 @@ func TestSession(outer *testing.T) { if !reflect.DeepEqual([]string{"1"}, rtx.Bookmarks) { t.Errorf("Using unclean or no bookmarks: %+v", rtx) } - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{"1"}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{"1"}) AssertIntEqual(t, bufferCalls, 1) }) @@ -473,7 +475,7 @@ func TestSession(outer *testing.T) { // Begin and commit a transaction on the session tx, _ := sess.BeginTransaction(context.Background()) tx.Commit(context.Background()) - AssertDeepEquals(t, BookmarksToRawValues(sess.LastBookmarks()), []string{bookmark}) + AssertDeepEquals(t, bm.BookmarksToRawValues(sess.LastBookmarks()), []string{bookmark}) // The bookmark should be used in next transaction sess.BeginTransaction(context.Background()) AssertLen(t, conn.RecordedTxs, 2) diff --git a/neo4j/test-integration/bookmark_test.go b/neo4j/test-integration/bookmark_test.go index f3bab2ad..865603c2 100644 --- a/neo4j/test-integration/bookmark_test.go +++ b/neo4j/test-integration/bookmark_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -52,7 +53,7 @@ func TestBookmark(outer *testing.T) { }) assertNil(outer, err) - bookmarks := neo4j.BookmarksToRawValues(session.LastBookmarks()) + bookmarks := bm.BookmarksToRawValues(session.LastBookmarks()) assertEquals(outer, len(bookmarks), 1) return bookmarks[0] } @@ -83,7 +84,7 @@ func TestBookmark(outer *testing.T) { _, err = result.Consume(ctx) assertNil(t, err) - assertStringsNotEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsNotEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is created in explicit transaction and committed, last bookmark should not be empty", func(t *testing.T) { @@ -102,7 +103,7 @@ func TestBookmark(outer *testing.T) { err = tx.Commit(ctx) assertNil(t, err) - assertStringsNotEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsNotEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is created in explicit transaction and rolled back, last bookmark should be empty", func(t *testing.T) { @@ -121,7 +122,7 @@ func TestBookmark(outer *testing.T) { err = tx.Rollback(ctx) assertNil(t, err) - assertStringsEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is created in transaction function, last bookmark should not be empty", func(t *testing.T) { @@ -140,7 +141,7 @@ func TestBookmark(outer *testing.T) { assertNil(t, err) assertEquals(t, result, 1) - assertStringsNotEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsNotEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is created in transaction function and rolled back, last bookmark should be empty", func(t *testing.T) { @@ -160,7 +161,7 @@ func TestBookmark(outer *testing.T) { assertEquals(t, err, failWith) assertNil(t, result) - assertStringsEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is queried in transaction function, last bookmark should not be empty", func(t *testing.T) { @@ -182,7 +183,7 @@ func TestBookmark(outer *testing.T) { assertNil(t, err) assertEquals(t, result, 1) - assertStringsNotEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsNotEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) inner.Run("when a node is created in transaction function and rolled back, last bookmark should be empty", func(t *testing.T) { @@ -205,7 +206,7 @@ func TestBookmark(outer *testing.T) { assertEquals(t, err, failWith) assertNil(t, result) - assertStringsEmpty(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) + assertStringsEmpty(t, bm.BookmarksToRawValues(session.LastBookmarks())) }) }) @@ -215,7 +216,7 @@ func TestBookmark(outer *testing.T) { bookmark := createNodeInTx(driver) session := driver.NewSession(ctx, neo4j.SessionConfig{ AccessMode: neo4j.AccessModeWrite, - Bookmarks: neo4j.BookmarksFromRawValues(bookmark), + Bookmarks: bm.BookmarksFromRawValues(bookmark), }) return driver, session, bookmark } @@ -238,7 +239,7 @@ func TestBookmark(outer *testing.T) { assertNil(t, err) defer tx.Close(ctx) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) }) inner.Run("given bookmarks should be accessible after ROLLBACK", func(t *testing.T) { @@ -255,7 +256,7 @@ func TestBookmark(outer *testing.T) { err = tx.Rollback(ctx) assertNil(t, err) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) }) inner.Run("given bookmarks should be accessible when transaction fails", func(t *testing.T) { @@ -271,7 +272,7 @@ func TestBookmark(outer *testing.T) { err = tx.Close(ctx) assertNil(t, err) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) }) inner.Run("given bookmarks should be accessible after run", func(t *testing.T) { @@ -284,7 +285,7 @@ func TestBookmark(outer *testing.T) { _, err = result.Consume(ctx) assertNil(t, err) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) }) inner.Run("given bookmarks should be accessible after failed run", func(t *testing.T) { @@ -294,7 +295,7 @@ func TestBookmark(outer *testing.T) { _, err := session.Run(ctx, "RETURN", nil) assertNotNil(t, err) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark}) }) }) @@ -315,7 +316,7 @@ func TestBookmark(outer *testing.T) { session = driver.NewSession(ctx, neo4j.SessionConfig{ AccessMode: neo4j.AccessModeWrite, - Bookmarks: neo4j.BookmarksFromRawValues(bookmark1, bookmark2), + Bookmarks: bm.BookmarksFromRawValues(bookmark1, bookmark2), }) defer func() { @@ -333,7 +334,7 @@ func TestBookmark(outer *testing.T) { assertNil(t, err) defer tx.Close(ctx) - assertEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark1, bookmark2}) + assertEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark1, bookmark2}) }) inner.Run("new bookmark should be reported back by the server after committing", func(t *testing.T) { @@ -351,9 +352,9 @@ func TestBookmark(outer *testing.T) { err = tx.Commit(ctx) assertNil(t, err) - assertNotNil(t, neo4j.BookmarksToRawValues(session.LastBookmarks())) - assertNotEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark1}) - assertNotEquals(t, neo4j.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark2}) + assertNotNil(t, bm.BookmarksToRawValues(session.LastBookmarks())) + assertNotEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark1}) + assertNotEquals(t, bm.BookmarksToRawValues(session.LastBookmarks()), []string{bookmark2}) }) }) @@ -367,7 +368,7 @@ func TestBookmark(outer *testing.T) { session := driver.NewSession(ctx, neo4j.SessionConfig{ AccessMode: neo4j.AccessModeWrite, - Bookmarks: neo4j.BookmarksFromRawValues(bookmark + "0"), + Bookmarks: bm.BookmarksFromRawValues(bookmark + "0"), }) return driver, session, bookmark } diff --git a/neo4j/test-integration/examples_test.go b/neo4j/test-integration/examples_test.go index c43399b2..4851fbbd 100644 --- a/neo4j/test-integration/examples_test.go +++ b/neo4j/test-integration/examples_test.go @@ -23,10 +23,12 @@ import ( "context" "errors" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "testing" "time" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" "github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" @@ -724,7 +726,7 @@ func printFriendsTxFunc(ctx context.Context) neo4j.ManagedTransactionWork { } } -func addAndEmploy(ctx context.Context, driver neo4j.DriverWithContext, person string, company string) (neo4j.Bookmarks, error) { +func addAndEmploy(ctx context.Context, driver neo4j.DriverWithContext, person string, company string) (bm.Bookmarks, error) { session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) defer session.Close(ctx) @@ -741,7 +743,7 @@ func addAndEmploy(ctx context.Context, driver neo4j.DriverWithContext, person st return session.LastBookmarks(), nil } -func makeFriend(ctx context.Context, driver neo4j.DriverWithContext, person1 string, person2 string, bookmarks neo4j.Bookmarks) (neo4j.Bookmarks, error) { +func makeFriend(ctx context.Context, driver neo4j.DriverWithContext, person1 string, person2 string, bookmarks bm.Bookmarks) (bm.Bookmarks, error) { session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite, Bookmarks: bookmarks}) defer session.Close(ctx) @@ -753,7 +755,7 @@ func makeFriend(ctx context.Context, driver neo4j.DriverWithContext, person1 str } func addEmployAndMakeFriends(ctx context.Context, driver neo4j.DriverWithContext) error { - var bookmarks1, bookmarks2, bookmarks3 neo4j.Bookmarks + var bookmarks1, bookmarks2, bookmarks3 bm.Bookmarks var err error if bookmarks1, err = addAndEmploy(ctx, driver, "Alice", "Wayne Enterprises"); err != nil { @@ -764,13 +766,13 @@ func addEmployAndMakeFriends(ctx context.Context, driver neo4j.DriverWithContext return err } - if bookmarks3, err = makeFriend(ctx, driver, "Bob", "Alice", neo4j.CombineBookmarks(bookmarks1, bookmarks2)); err != nil { + if bookmarks3, err = makeFriend(ctx, driver, "Bob", "Alice", bm.CombineBookmarks(bookmarks1, bookmarks2)); err != nil { return err } session := driver.NewSession(ctx, neo4j.SessionConfig{ AccessMode: neo4j.AccessModeRead, - Bookmarks: neo4j.CombineBookmarks(bookmarks1, bookmarks2, bookmarks3), + Bookmarks: bm.CombineBookmarks(bookmarks1, bookmarks2, bookmarks3), }) defer session.Close(ctx) diff --git a/neo4j/test-integration/summary_test.go b/neo4j/test-integration/summary_test.go index ee16a501..5d2d3600 100644 --- a/neo4j/test-integration/summary_test.go +++ b/neo4j/test-integration/summary_test.go @@ -2,10 +2,12 @@ package test_integration import ( "context" + "testing" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" - "testing" ) func TestResultSummary(outer *testing.T) { @@ -33,7 +35,7 @@ func TestResultSummary(outer *testing.T) { } inner.Run("does not include any database information", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: neo4j.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) @@ -59,12 +61,12 @@ func TestResultSummary(outer *testing.T) { assertNil(inner, err) _, err = res.Consume(ctx) // consume result to obtain bookmark assertNil(inner, err) - bookmarks := neo4j.BookmarksToRawValues(session.LastBookmarks()) + bookmarks := bm.BookmarksToRawValues(session.LastBookmarks()) assertEquals(inner, len(bookmarks), 1) bookmark = bookmarks[0] defer func() { - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "system", Bookmarks: neo4j.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "system", Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, inner, session) res, err := session.Run(ctx, server.DropDatabaseQuery(extraDatabase), map[string]any{}) assertNil(inner, err) @@ -74,7 +76,7 @@ func TestResultSummary(outer *testing.T) { }() inner.Run("includes the default database information", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: neo4j.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) @@ -85,7 +87,7 @@ func TestResultSummary(outer *testing.T) { }) inner.Run("includes the database information, based on session configuration", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: extraDatabase, Bookmarks: neo4j.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: extraDatabase, Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) diff --git a/neo4j/transaction_helpers_test.go b/neo4j/transaction_helpers_test.go index 3100e170..a0e7005c 100644 --- a/neo4j/transaction_helpers_test.go +++ b/neo4j/transaction_helpers_test.go @@ -22,9 +22,11 @@ package neo4j_test import ( "context" "fmt" + "testing" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" - "testing" ) func TestExecuteRead(outer *testing.T) { @@ -81,7 +83,7 @@ type fakeSession struct { neo4j.SessionWithContext } -func (f *fakeSession) LastBookmarks() neo4j.Bookmarks { +func (f *fakeSession) LastBookmarks() bm.Bookmarks { panic("implement me") } diff --git a/test-stress/testcontext.go b/test-stress/testcontext.go index 84fc554c..dd123cdd 100644 --- a/test-stress/testcontext.go +++ b/test-stress/testcontext.go @@ -25,6 +25,7 @@ import ( "sync/atomic" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" ) // TestContext provides state data shared across tests @@ -50,7 +51,7 @@ func NewTestContext(driver neo4j.DriverWithContext) *TestContext { createdNodeCount: 0, } - result.bookmarks.Store(neo4j.Bookmarks{}) + result.bookmarks.Store(bm.Bookmarks{}) return result } @@ -73,11 +74,11 @@ func (ctx *TestContext) addRead() { atomic.AddInt32(&ctx.readNodeCount, 1) } -func (ctx *TestContext) getBookmarks() neo4j.Bookmarks { - return ctx.bookmarks.Load().(neo4j.Bookmarks) +func (ctx *TestContext) getBookmarks() bm.Bookmarks { + return ctx.bookmarks.Load().(bm.Bookmarks) } -func (ctx *TestContext) setBookmarks(bookmarks neo4j.Bookmarks) { +func (ctx *TestContext) setBookmarks(bookmarks bm.Bookmarks) { ctx.bookmarks.Store(bookmarks) } diff --git a/testkit-backend/backend.go b/testkit-backend/backend.go index b082ed40..719c6344 100644 --- a/testkit-backend/backend.go +++ b/testkit-backend/backend.go @@ -25,8 +25,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" "io" "math" "net/url" @@ -35,6 +33,10 @@ import ( "sync" "time" + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" "github.com/neo4j/neo4j-go-driver/v5/neo4j/auth" "github.com/neo4j/neo4j-go-driver/v5/neo4j/db" @@ -59,9 +61,9 @@ type backend struct { resolvedBearerTokens map[string]AuthTokenAndExpiration id int // ID to use for next object created by frontend wrLock sync.Mutex - suppliedBookmarks map[string]neo4j.Bookmarks + suppliedBookmarks map[string]bm.Bookmarks consumedBookmarks map[string]struct{} - bookmarkManagers map[string]neo4j.BookmarkManager + bookmarkManagers map[string]bm.BookmarkManager timer *Timer } @@ -133,8 +135,8 @@ func newBackend(rd *bufio.Reader, wr io.Writer) *backend { resolvedBasicTokens: make(map[string]AuthToken), resolvedBearerTokens: make(map[string]AuthTokenAndExpiration), id: 0, - bookmarkManagers: make(map[string]neo4j.BookmarkManager), - suppliedBookmarks: make(map[string]neo4j.Bookmarks), + bookmarkManagers: make(map[string]bm.BookmarkManager), + suppliedBookmarks: make(map[string]bm.Bookmarks), consumedBookmarks: make(map[string]struct{}), } } @@ -450,7 +452,7 @@ func (b *backend) handleRequest(req map[string]any) { case "BookmarksSupplierCompleted": requestId := data["requestId"].(string) rawBookmarks := data["bookmarks"].([]any) - bookmarks := make(neo4j.Bookmarks, len(rawBookmarks)) + bookmarks := make(bm.Bookmarks, len(rawBookmarks)) for i, bookmark := range rawBookmarks { bookmarks[i] = bookmark.(string) } @@ -640,7 +642,7 @@ func (b *backend) handleRequest(req map[string]any) { for i, x := range rawBookmarks { bookmarks[i] = x.(string) } - sessionConfig.Bookmarks = neo4j.BookmarksFromRawValues(bookmarks...) + sessionConfig.Bookmarks = bm.BookmarksFromRawValues(bookmarks...) } if data["database"] != nil { sessionConfig.DatabaseName = data["database"].(string) @@ -693,7 +695,7 @@ func (b *backend) handleRequest(req map[string]any) { case "NewBookmarkManager": bookmarkManagerId := b.nextId() - b.bookmarkManagers[bookmarkManagerId] = neo4j.NewBookmarkManager( + b.bookmarkManagers[bookmarkManagerId] = bm.NewBookmarkManager( b.bookmarkManagerConfig(bookmarkManagerId, data)) b.writeResponse("BookmarkManager", map[string]any{ "id": bookmarkManagerId, @@ -750,7 +752,7 @@ func (b *backend) handleRequest(req map[string]any) { case "SessionLastBookmarks": sessionState := b.sessionStates[data["sessionId"].(string)] - bookmarks := neo4j.BookmarksToRawValues(sessionState.session.LastBookmarks()) + bookmarks := bm.BookmarksToRawValues(sessionState.session.LastBookmarks()) if bookmarks == nil { bookmarks = []string{} } @@ -1585,13 +1587,13 @@ func patchNumbersInMap(dictionary map[string]any) error { } func (b *backend) bookmarkManagerConfig(bookmarkManagerId string, - config map[string]any) neo4j.BookmarkManagerConfig { + config map[string]any) bm.BookmarkManagerConfig { - var initialBookmarks neo4j.Bookmarks + var initialBookmarks bm.Bookmarks if config["initialBookmarks"] != nil { initialBookmarks = convertInitialBookmarks(config["initialBookmarks"].([]any)) } - result := neo4j.BookmarkManagerConfig{InitialBookmarks: initialBookmarks} + result := bm.BookmarkManagerConfig{InitialBookmarks: initialBookmarks} supplierRegistered := config["bookmarksSupplierRegistered"] if supplierRegistered != nil && supplierRegistered.(bool) { result.BookmarkSupplier = b.supplyBookmarks(bookmarkManagerId) @@ -1603,8 +1605,8 @@ func (b *backend) bookmarkManagerConfig(bookmarkManagerId string, return result } -func (b *backend) supplyBookmarks(bookmarkManagerId string) func(context.Context) (neo4j.Bookmarks, error) { - return func(ctx context.Context) (neo4j.Bookmarks, error) { +func (b *backend) supplyBookmarks(bookmarkManagerId string) func(context.Context) (bm.Bookmarks, error) { + return func(ctx context.Context) (bm.Bookmarks, error) { id := b.nextId() msg := map[string]any{"id": id, "bookmarkManagerId": bookmarkManagerId} b.writeResponse("BookmarksSupplierRequest", msg) @@ -1613,8 +1615,8 @@ func (b *backend) supplyBookmarks(bookmarkManagerId string) func(context.Context } } -func (b *backend) consumeBookmarks(bookmarkManagerId string) func(context.Context, neo4j.Bookmarks) error { - return func(_ context.Context, bookmarks neo4j.Bookmarks) error { +func (b *backend) consumeBookmarks(bookmarkManagerId string) func(context.Context, bm.Bookmarks) error { + return func(_ context.Context, bookmarks bm.Bookmarks) error { id := b.nextId() b.writeResponse("BookmarksConsumerRequest", map[string]any{ "id": id, @@ -1631,8 +1633,8 @@ func (b *backend) consumeBookmarks(bookmarkManagerId string) func(context.Contex } } -func convertInitialBookmarks(bookmarks []any) neo4j.Bookmarks { - result := make(neo4j.Bookmarks, len(bookmarks)) +func convertInitialBookmarks(bookmarks []any) bm.Bookmarks { + result := make(bm.Bookmarks, len(bookmarks)) for i, bookmark := range bookmarks { result[i] = bookmark.(string) } From f2a6adfdcbf8f93685aed43bebba9cee3b6cbb55 Mon Sep 17 00:00:00 2001 From: Luca Pirolo <66700259+lucapirolo@users.noreply.github.com> Date: Sun, 26 Nov 2023 18:14:02 +0000 Subject: [PATCH 2/5] Refactor: Move Session Config Into config Package - Relocated all session config related code to a new 'bookmarks' package. - Add AcessMode type to config package - Add AuthToken type alias to config package --- README.md | 4 +- benchmark/main.go | 11 +- neo4j/auth/auth_examples_test.go | 5 +- neo4j/bookmarks/bookmarks.go | 2 +- neo4j/config/driver.go | 118 ++++++++++++++++++- neo4j/driver.go | 6 +- neo4j/driver_test.go | 17 +-- neo4j/driver_with_context.go | 21 ++-- neo4j/driver_with_context_examples_test.go | 4 +- neo4j/driver_with_context_test.go | 21 ++-- neo4j/graph_example_test.go | 4 +- neo4j/graph_test.go | 5 +- neo4j/notifications/notifications.go | 2 +- neo4j/notifications_examples_test.go | 10 +- neo4j/record_example_test.go | 4 +- neo4j/session_with_context.go | 107 +---------------- neo4j/session_with_context_test.go | 15 +-- neo4j/test-integration/auth_test.go | 3 +- neo4j/test-integration/bookmark_test.go | 17 +-- neo4j/test-integration/context_test.go | 8 +- neo4j/test-integration/dbserver/dbserver.go | 4 +- neo4j/test-integration/driver_test.go | 19 +-- neo4j/test-integration/examples_test.go | 38 +++--- neo4j/test-integration/multidatabase_test.go | 10 +- neo4j/test-integration/routing_test.go | 5 +- neo4j/test-integration/session_test.go | 25 ++-- neo4j/test-integration/summary_test.go | 10 +- neo4j/test-integration/timeout_test.go | 9 +- neo4j/test-integration/transaction_test.go | 9 +- neo4j/test-integration/types_test.go | 3 +- neo4j/test-integration/utils_test.go | 8 +- neo4j/transaction_helpers_example_test.go | 8 +- test-stress/bigdata.go | 3 +- test-stress/clean.go | 4 +- test-stress/executors.go | 37 +++--- testkit-backend/backend.go | 8 +- 36 files changed, 317 insertions(+), 267 deletions(-) diff --git a/README.md b/README.md index e2b80b44..a7749116 100644 --- a/README.md +++ b/README.md @@ -337,12 +337,12 @@ boltLogger := neo4j.ConsoleBoltLogger() result, err := neo4j.ExecuteQuery(ctx, driver, query, params, transformer, neo4j.ExecuteQueryWithBoltLogger(boltLogger)) # for the regular session APIs (session.Run, session.BeginTransaction, session.ExecuteRead, session.ExecuteWrite) -session := driver.NewSession(neo4j.SessionConfig{BoltLogger: boltLogger}) +session := driver.NewSession(config.SessionConfig{BoltLogger: boltLogger}) ``` ### Custom Bolt Logger -The `BoltLogger` field of the `neo4j.SessionConfig` struct is defined to be of interface `neo4j/log.BoltLogger` which has the following definition: +The `BoltLogger` field of the `config.SessionConfig` struct is defined to be of interface `neo4j/log.BoltLogger` which has the following definition: ```go type BoltLogger interface { diff --git a/benchmark/main.go b/benchmark/main.go index 62003486..a484c6ab 100644 --- a/benchmark/main.go +++ b/benchmark/main.go @@ -27,11 +27,12 @@ import ( neo4j18 "github.com/neo4j/neo4j-go-driver/neo4j" neo4j "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func getSetup(driver neo4j.Driver) *neo4j.Node { // Check if setup already built - sess := driver.NewSession(neo4j.SessionConfig{}) + sess := driver.NewSession(config.SessionConfig{}) defer sess.Close() result, err := sess.Run("MATCH (s:Setup) RETURN s", nil) @@ -71,7 +72,7 @@ func getBoolProp(node *neo4j.Node, name string, dflt bool) bool { func buildSetup(driver neo4j.Driver, setup *neo4j.Node) { - sess := driver.NewSession(neo4j.SessionConfig{}) + sess := driver.NewSession(config.SessionConfig{}) defer sess.Close() if !getBoolProp(setup, "iterMxL", false) { @@ -95,7 +96,7 @@ func buildSetup(driver neo4j.Driver, setup *neo4j.Node) { } func iterMxL(driver neo4j.Driver) { - sess := driver.NewSession(neo4j.SessionConfig{}) + sess := driver.NewSession(config.SessionConfig{}) defer sess.Close() result, err := sess.Run("MATCH (n:IterMxL) RETURN n", nil) @@ -153,7 +154,7 @@ func buildParamsLMap() map[string]any { func params(driver neo4j.Driver, m map[string]any, n int) { // Use same session for all of n, not part of measurement - session := driver.NewSession(neo4j.SessionConfig{}) + session := driver.NewSession(config.SessionConfig{}) for i := 0; i < n; i++ { _, err := session.Run("RETURN 0", m) if err != nil { @@ -179,7 +180,7 @@ func params18(driver neo4j18.Driver, m map[string]any, n int) { // Include session creation in measurement func getS(driver neo4j.Driver, n int) { for i := 0; i < n; i++ { - session := driver.NewSession(neo4j.SessionConfig{}) + session := driver.NewSession(config.SessionConfig{}) x, _ := session.ReadTransaction(func(tx neo4j.Transaction) (any, error) { res, err := tx.Run("RETURN $i", map[string]any{"i": i}) if err != nil { diff --git a/neo4j/auth/auth_examples_test.go b/neo4j/auth/auth_examples_test.go index a87838bc..ffccf69b 100644 --- a/neo4j/auth/auth_examples_test.go +++ b/neo4j/auth/auth_examples_test.go @@ -20,10 +20,11 @@ package auth_test import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/auth" "os" "time" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/auth" ) func ExampleBasicTokenManager() { diff --git a/neo4j/bookmarks/bookmarks.go b/neo4j/bookmarks/bookmarks.go index 408ceb7d..60d5e459 100644 --- a/neo4j/bookmarks/bookmarks.go +++ b/neo4j/bookmarks/bookmarks.go @@ -112,7 +112,7 @@ func NewBookmarkManager(config BookmarkManagerConfig) BookmarkManager { // Let s1, s2, s3 be Session interfaces. You can easily causally chain the sessions like so: // ```go // -// s4 := driver.NewSession(neo4j.SessionConfig{ +// s4 := driver.NewSession(config.SessionConfig{ // Bookmarks: neo4j.CombineBookmarks(s1.LastBookmarks(), s2.LastBookmarks(), s3.LastBookmarks()), // }) // diff --git a/neo4j/config/driver.go b/neo4j/config/driver.go index 3ca58512..d622160f 100644 --- a/neo4j/config/driver.go +++ b/neo4j/config/driver.go @@ -20,9 +20,24 @@ package config import ( "crypto/tls" "crypto/x509" + "time" + + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/auth" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" - "time" +) + +// AccessMode defines modes that routing driver decides to which cluster member +// a connection should be opened. +type AccessMode int +type AuthToken = auth.Token + +const ( + // AccessModeWrite tells the driver to use a connection to 'Leader' + AccessModeWrite AccessMode = 0 + // AccessModeRead tells the driver to use a connection to one of the 'Follower' or 'Read Replica'. + AccessModeRead AccessMode = 1 ) // A Config contains options that can be used to customize certain @@ -174,3 +189,104 @@ type ServerAddress interface { // Port returns the port portion of this ServerAddress. Port() string } + +// SessionConfig is used to configure a new session, its zero value uses safe defaults. +type SessionConfig struct { + // AccessMode used when using Session.Run and explicit transactions. Used to route query + // to read or write servers when running in a cluster. Session.ReadTransaction and Session.WriteTransaction + // does not rely on this mode. + AccessMode AccessMode + // Bookmarks are the initial bookmarks used to ensure that the executing server is at least up + // to date to the point represented by the latest of the provided bookmarks. After running commands + // on the session the bookmark can be retrieved with Session.LastBookmark. All commands executing + // within the same session will automatically use the bookmark from the previous command in the + // session. + Bookmarks bm.Bookmarks + // DatabaseName sets the target database name for the queries executed within the session created with this + // configuration. + // Usage of Cypher clauses like USE is not a replacement for this option. + // Drive​r sends Cypher to the server for processing. + // This option has no explicit value by default, but it is recommended to set one if the target database is known + // in advance. This has the benefit of ensuring a consistent target database name throughout the session in a + // straightforward way and potentially simplifies driver logic as well as reduces network communication resulting + // in better performance. + // When no explicit name is set, the driver behavior depends on the connection URI scheme supplied to the driver on + // instantiation and Bolt protocol version. + // + // Specifically, the following applies: + // + // - for bolt schemes + // queries are dispatched to the server for execution without explicit database name supplied, + // meaning that the target database name for query execution is determined by the server. + // It is important to note that the target database may change (even within the same session), for instance if the + // user's home database is changed on the server. + // + // - for neo4j schemes + // providing that Bolt protocol version 4.4, which was introduced with Neo4j server 4.4, or above + // is available, the driver fetches the user's home database name from the server on first query execution + // within the session and uses the fetched database name explicitly for all queries executed within the session. + // This ensures that the database name remains consistent within the given session. For instance, if the user's + // home database name is 'movies' and the server supplies it to the driver upon database name fetching for the + // session, all queries within that session are executed with the explicit database name 'movies' supplied. + // Any change to the user’s home database is reflected only in sessions created after such change takes effect. + // This behavior requires additional network communication. + // In clustered environments, it is strongly recommended to avoid a single point of failure. + // For instance, by ensuring that the connection URI resolves to multiple endpoints. + // For older Bolt protocol versions, the behavior is the same as described for the bolt schemes above. + DatabaseName string + // FetchSize defines how many records to pull from server in each batch. + // From Bolt protocol v4 (Neo4j 4+) records can be fetched in batches as compared to fetching + // all in previous versions. + // + // If FetchSize is set to FetchDefault, the driver decides the appropriate size. If set to a positive value + // that size is used if the underlying protocol supports it otherwise it is ignored. + // + // To turn off fetching in batches and always fetch everything, set FetchSize to FetchAll. + // If a single large result is to be retrieved this is the most performant setting. + FetchSize int + // Logging target the session will send its Bolt message traces + // + // Possible to use custom logger (implement log.BoltLogger interface) or + // use neo4j.ConsoleBoltLogger. + BoltLogger log.BoltLogger + // ImpersonatedUser sets the Neo4j user that the session will be acting as. + // If not set, the user configured for the driver will be used. + // + // If user impersonation is used, the default database for that impersonated + // user will be used unless DatabaseName is set. + // + // In the former case, when routing is enabled, using impersonation + // without DatabaseName will cause the driver to query the + // cluster for the name of the default database of the impersonated user. + // This is done at the beginning of the session so that queries are routed + // to the correct cluster member (different databases may have different + // leaders). + ImpersonatedUser string + // BookmarkManager defines a central point to externally supply bookmarks + // and be notified of bookmark updates per database + // Since 5.0 + // default: nil (no-op) + BookmarkManager bm.BookmarkManager + // NotificationsMinSeverity defines the minimum severity level of notifications the server should send. + // By default, the driver's settings are used. + // Else, this option overrides the driver's settings. + // Disabling severities allows the server to skip analysis for those, which can speed up query execution. + NotificationsMinSeverity notifications.NotificationMinimumSeverityLevel + // NotificationsDisabledCategories defines the categories of notifications the server should not send. + // By default, the driver's settings are used. + // Else, this option overrides the driver's settings. + // Disabling categories allows the server to skip analysis for those, which can speed up query execution. + NotificationsDisabledCategories notifications.NotificationDisabledCategories + // Auth is used to overwrite the authentication information for the session. + // This requires the server to support re-authentication on the protocol level. + // `nil` will make the driver use the authentication information from the driver configuration. + // The `neo4j` package provides factory functions for common authentication schemes: + // - `neo4j.NoAuth` + // - `neo4j.BasicAuth` + // - `neo4j.KerberosAuth` + // - `neo4j.BearerAuth` + // - `neo4j.CustomAuth` + Auth *AuthToken + + ForceReAuth bool +} diff --git a/neo4j/driver.go b/neo4j/driver.go index aad0a1ab..2f8d850c 100644 --- a/neo4j/driver.go +++ b/neo4j/driver.go @@ -21,6 +21,8 @@ package neo4j import ( "context" "net/url" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) // Driver represents a pool(s) of connections to a neo4j server or cluster. It's @@ -31,7 +33,7 @@ type Driver interface { // Target returns the url this driver is bootstrapped Target() url.URL // NewSession creates a new session based on the specified session configuration. - NewSession(config SessionConfig) Session + NewSession(config config.SessionConfig) Session // VerifyConnectivity checks that the driver can connect to a remote server or cluster by // establishing a network connection with the remote. Returns nil if succesful // or error describing the problem. @@ -80,7 +82,7 @@ func (d *driver) Target() url.URL { return d.delegate.Target() } -func (d *driver) NewSession(config SessionConfig) Session { +func (d *driver) NewSession(config config.SessionConfig) Session { return d.delegate.NewSession(context.Background(), config).legacy() } diff --git a/neo4j/driver_test.go b/neo4j/driver_test.go index f8bbd4eb..a31f3271 100644 --- a/neo4j/driver_test.go +++ b/neo4j/driver_test.go @@ -22,6 +22,7 @@ import ( "testing" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/router" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) @@ -198,7 +199,7 @@ func TestNewDriverAndClose(t *testing.T) { err = driver.Close() AssertNoError(t, err) - session := driver.NewSession(SessionConfig{}) + session := driver.NewSession(config.SessionConfig{}) _, err = session.Run("cypher", nil) if !IsUsageError(err) { t.Errorf("should not allow new session after driver being closed") @@ -214,13 +215,13 @@ func TestDriverSessionCreation(t *testing.T) { driverSessionCreationTests := []struct { name string testing string - mode AccessMode + mode config.AccessMode bookmarks bm.Bookmarks }{ - {"Write", "bolt://localhost:7687", AccessModeWrite, []string(nil)}, - {"Read", "bolt://localhost:7687", AccessModeRead, []string(nil)}, - {"Write+bookmarks", "bolt://localhost:7687", AccessModeWrite, []string{"B1", "B2", "B3"}}, - {"Read+bookmarks", "bolt://localhost:7687", AccessModeRead, []string{"B1", "B2", "B3", "B4"}}, + {"Write", "bolt://localhost:7687", config.AccessModeWrite, []string(nil)}, + {"Read", "bolt://localhost:7687", config.AccessModeWrite, []string(nil)}, + {"Write+bookmarks", "bolt://localhost:7687", config.AccessModeWrite, []string{"B1", "B2", "B3"}}, + {"Read+bookmarks", "bolt://localhost:7687", config.AccessModeWrite, []string{"B1", "B2", "B3", "B4"}}, } for _, tt := range driverSessionCreationTests { @@ -228,10 +229,10 @@ func TestDriverSessionCreation(t *testing.T) { driver, err := NewDriver(tt.testing, NoAuth()) AssertNoError(t, err) - sessi := driver.NewSession(SessionConfig{AccessMode: tt.mode, Bookmarks: tt.bookmarks}) + sessi := driver.NewSession(config.SessionConfig{AccessMode: tt.mode, Bookmarks: tt.bookmarks}) sess := sessi.(*session) - if AccessMode(sess.delegate.defaultMode) != tt.mode { + if config.AccessMode(sess.delegate.defaultMode) != tt.mode { t.Errorf("the defaultMode was not correctly set %v", AccessMode(sess.delegate.defaultMode)) } diff --git a/neo4j/driver_with_context.go b/neo4j/driver_with_context.go index 0f880466..a14086ab 100644 --- a/neo4j/driver_with_context.go +++ b/neo4j/driver_with_context.go @@ -28,6 +28,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j/auth" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/racing" @@ -61,13 +62,13 @@ type DriverWithContext interface { // // [...] do something with results and error // bookmarkManager := driver.ExecuteQueryBookmarkManager() // // maintain consistency with sessions as well - // session := driver.NewSession(ctx, neo4j.SessionConfig {BookmarkManager: bookmarkManager}) + // session := driver.NewSession(ctx, config.SessionConfig {BookmarkManager: bookmarkManager}) // // [...] run something within the session ExecuteQueryBookmarkManager() bm.BookmarkManager // Target returns the url this driver is bootstrapped Target() url.URL // NewSession creates a new session based on the specified session configuration. - NewSession(ctx context.Context, config SessionConfig) SessionWithContext + NewSession(ctx context.Context, config config.SessionConfig) SessionWithContext // VerifyConnectivity checks that the driver can connect to a remote server or cluster by // establishing a network connection with the remote. Returns nil if successful // or error describing the problem. @@ -333,7 +334,7 @@ func (d *driverWithContext) Target() url.URL { return *d.target } -func (d *driverWithContext) NewSession(ctx context.Context, config SessionConfig) SessionWithContext { +func (d *driverWithContext) NewSession(ctx context.Context, config config.SessionConfig) SessionWithContext { if config.DatabaseName == "" { config.DatabaseName = idb.DefaultDatabase } @@ -343,13 +344,13 @@ func (d *driverWithContext) NewSession(ctx context.Context, config SessionConfig reAuthToken = &idb.ReAuthToken{ Manager: d.auth, FromSession: false, - ForceReAuth: config.forceReAuth, + ForceReAuth: config.ForceReAuth, } } else { reAuthToken = &idb.ReAuthToken{ Manager: config.Auth, FromSession: true, - ForceReAuth: config.forceReAuth, + ForceReAuth: config.ForceReAuth, } } @@ -375,7 +376,7 @@ func (d *driverWithContext) IsEncrypted() bool { } func (d *driverWithContext) GetServerInfo(ctx context.Context) (_ ServerInfo, err error) { - session := d.NewSession(ctx, SessionConfig{}) + session := d.NewSession(ctx, config.SessionConfig{}) defer func() { err = deferredClose(ctx, session, err) }() @@ -397,7 +398,7 @@ func (d *driverWithContext) Close(ctx context.Context) error { } func (d *driverWithContext) VerifyAuthentication(ctx context.Context, auth *AuthToken) (err error) { - session := d.NewSession(ctx, SessionConfig{Auth: auth, forceReAuth: true, DatabaseName: "system"}) + session := d.NewSession(ctx, config.SessionConfig{Auth: auth, ForceReAuth: true, DatabaseName: "system"}) defer func() { err = deferredClose(ctx, session, err) }() @@ -472,7 +473,7 @@ func (d *driverWithContext) VerifyAuthentication(ctx context.Context, auth *Auth // The equivalent functionality of ExecuteQuery can be replicated with pre-existing APIs as follows: // // // all the error handling bits have been omitted for brevity (do not do this in production!) -// session := driver.NewSession(ctx, neo4j.SessionConfig{ +// session := driver.NewSession(ctx, config.SessionConfig{ // DatabaseName: "", // ImpersonatedUser: "", // BookmarkManager: bookmarkManager, @@ -681,8 +682,8 @@ const ( Read ) -func (c *ExecuteQueryConfiguration) toSessionConfig() SessionConfig { - return SessionConfig{ +func (c *ExecuteQueryConfiguration) toSessionConfig() config.SessionConfig { + return config.SessionConfig{ ImpersonatedUser: c.ImpersonatedUser, DatabaseName: c.Database, BookmarkManager: c.BookmarkManager, diff --git a/neo4j/driver_with_context_examples_test.go b/neo4j/driver_with_context_examples_test.go index e3854091..01822624 100644 --- a/neo4j/driver_with_context_examples_test.go +++ b/neo4j/driver_with_context_examples_test.go @@ -21,6 +21,8 @@ import ( "context" "errors" "fmt" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) var myDriver DriverWithContext @@ -82,7 +84,7 @@ func ExampleExecuteQuery_defaultBookmarkManagerExplicitReuse() { // retrieve the default bookmark manager used by the previous call (since there was no bookmark manager explicitly // configured) bookmarkManager := myDriver.ExecuteQueryBookmarkManager() - session := myDriver.NewSession(ctx, SessionConfig{BookmarkManager: bookmarkManager}) + session := myDriver.NewSession(ctx, config.SessionConfig{BookmarkManager: bookmarkManager}) // the following transaction function is guaranteed to see the result of the previous query // since the session uses the same bookmark manager as the previous ExecuteQuery call and targets the same diff --git a/neo4j/driver_with_context_test.go b/neo4j/driver_with_context_test.go index 75f34ac2..d81003db 100644 --- a/neo4j/driver_with_context_test.go +++ b/neo4j/driver_with_context_test.go @@ -29,6 +29,7 @@ import ( "unsafe" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/racing" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) @@ -44,7 +45,7 @@ func TestDriverExecuteQuery(outer *testing.T) { summary := &fakeSummary{resultAvailableAfter: 42 * time.Millisecond} defaultBookmarkManager := &fakeBookmarkManager{} customBookmarkManager := &fakeBookmarkManager{} - defaultSessionConfig := SessionConfig{BookmarkManager: defaultBookmarkManager} + defaultSessionConfig := config.SessionConfig{BookmarkManager: defaultBookmarkManager} outer.Run("nil driver is not allowed", func(t *testing.T) { _, err := ExecuteQuery(ctx, nil, "RETURN 42", nil, EagerResultTransformer) @@ -57,7 +58,7 @@ func TestDriverExecuteQuery(outer *testing.T) { resultTransformer func() ResultTransformer[T] configurers []ExecuteQueryConfigurationOption createSession *fakeSession - expectedSessionConfig SessionConfig + expectedSessionConfig config.SessionConfig expectedResult T expectedErr error } @@ -90,7 +91,7 @@ func TestDriverExecuteQuery(outer *testing.T) { nextRecords: records, summary: summary, }}, - expectedSessionConfig: SessionConfig{ImpersonatedUser: "jane", BookmarkManager: defaultBookmarkManager}, + expectedSessionConfig: config.SessionConfig{ImpersonatedUser: "jane", BookmarkManager: defaultBookmarkManager}, expectedResult: &EagerResult{ Keys: keys, Records: records, @@ -108,7 +109,7 @@ func TestDriverExecuteQuery(outer *testing.T) { nextRecords: records, summary: summary, }}, - expectedSessionConfig: SessionConfig{DatabaseName: "imdb", BookmarkManager: defaultBookmarkManager}, + expectedSessionConfig: config.SessionConfig{DatabaseName: "imdb", BookmarkManager: defaultBookmarkManager}, expectedResult: &EagerResult{ Keys: keys, Records: records, @@ -126,7 +127,7 @@ func TestDriverExecuteQuery(outer *testing.T) { nextRecords: records, summary: summary, }}, - expectedSessionConfig: SessionConfig{BookmarkManager: customBookmarkManager}, + expectedSessionConfig: config.SessionConfig{BookmarkManager: customBookmarkManager}, expectedResult: &EagerResult{ Keys: keys, Records: records, @@ -144,7 +145,7 @@ func TestDriverExecuteQuery(outer *testing.T) { nextRecords: records, summary: summary, }}, - expectedSessionConfig: SessionConfig{BookmarkManager: nil}, + expectedSessionConfig: config.SessionConfig{BookmarkManager: nil}, expectedResult: &EagerResult{ Keys: keys, Records: records, @@ -411,7 +412,7 @@ func TestDriverExecuteQuery(outer *testing.T) { for _, testCase := range testCases { outer.Run(testCase.description, func(t *testing.T) { driver := &driverDelegate{ - newSession: func(_ context.Context, config SessionConfig) SessionWithContext { + newSession: func(_ context.Context, config config.SessionConfig) SessionWithContext { AssertDeepEquals(t, testCase.expectedSessionConfig, config) return testCase.createSession }, @@ -431,7 +432,7 @@ func TestDriverExecuteQuery(outer *testing.T) { outer.Run("default bookmark manager is thread-safe", func(t *testing.T) { driver := &driverDelegate{ - newSession: func(_ context.Context, config SessionConfig) SessionWithContext { + newSession: func(_ context.Context, config config.SessionConfig) SessionWithContext { return &fakeSession{ executeWriteErr: fmt.Errorf("oopsie, write failed"), } @@ -516,7 +517,7 @@ func (f *failingResultTransformer) Complete([]string, ResultSummary) (*EagerResu type driverDelegate struct { delegate *driverWithContext - newSession func(context.Context, SessionConfig) SessionWithContext + newSession func(context.Context, config.SessionConfig) SessionWithContext } func (d *driverDelegate) ExecuteQueryBookmarkManager() bm.BookmarkManager { @@ -527,7 +528,7 @@ func (d *driverDelegate) Target() url.URL { return d.delegate.Target() } -func (d *driverDelegate) NewSession(ctx context.Context, config SessionConfig) SessionWithContext { +func (d *driverDelegate) NewSession(ctx context.Context, config config.SessionConfig) SessionWithContext { return d.newSession(ctx, config) } diff --git a/neo4j/graph_example_test.go b/neo4j/graph_example_test.go index 4ce5865f..9463b9dd 100644 --- a/neo4j/graph_example_test.go +++ b/neo4j/graph_example_test.go @@ -20,7 +20,9 @@ package neo4j_test import ( "context" "fmt" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func ExampleGetProperty() { @@ -28,7 +30,7 @@ func ExampleGetProperty() { driver, err := createDriver() handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer handleClose(ctx, session) result, err := session.Run(ctx, "MATCH (p:Person) RETURN p LIMIT 1", nil) diff --git a/neo4j/graph_test.go b/neo4j/graph_test.go index 98219c79..e0f048bf 100644 --- a/neo4j/graph_test.go +++ b/neo4j/graph_test.go @@ -19,11 +19,12 @@ package neo4j_test import ( "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" - . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" "testing" "testing/quick" "time" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) func TestGetProperty(outer *testing.T) { diff --git a/neo4j/notifications/notifications.go b/neo4j/notifications/notifications.go index 1252e59e..3838f553 100644 --- a/neo4j/notifications/notifications.go +++ b/neo4j/notifications/notifications.go @@ -44,7 +44,7 @@ func DisableCategories(value ...NotificationCategory) NotificationDisabledCatego } // DisableNoCategories creates a NotificationDisabledCategories that enables all categories. -// Can be used for NotificationsDisabledCategories of neo4j.Config and neo4j.SessionConfig. +// Can be used for NotificationsDisabledCategories of neo4j.Config and config.SessionConfig. func DisableNoCategories() NotificationDisabledCategories { return NotificationDisabledCategories{nil, true} } diff --git a/neo4j/notifications_examples_test.go b/neo4j/notifications_examples_test.go index 4b772127..dcc1eb68 100644 --- a/neo4j/notifications_examples_test.go +++ b/neo4j/notifications_examples_test.go @@ -20,8 +20,10 @@ package neo4j import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" "os" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" ) func ExampleConfig_disableNoCategories() { @@ -49,7 +51,7 @@ func ExampleSessionConfig_disableNoCategories() { handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, SessionConfig{ + session := driver.NewSession(ctx, config.SessionConfig{ // makes the server return all notification categories // this overrides the driver level configuration of the same name NotificationsDisabledCategories: notifications.DisableNoCategories(), @@ -94,7 +96,7 @@ func ExampleSessionConfig_disableSomeCategories() { handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, SessionConfig{ + session := driver.NewSession(ctx, config.SessionConfig{ // makes the server return all notification categories but deprecations // this overrides the driver level configuration of the same name NotificationsDisabledCategories: notifications.DisableCategories(notifications.Deprecation), @@ -140,7 +142,7 @@ func ExampleSessionConfig_minimumSeverityLevel() { handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, SessionConfig{ + session := driver.NewSession(ctx, config.SessionConfig{ // makes the server return only notifications with severity level warning or higher // this overrides the driver level configuration of the same name NotificationsMinSeverity: notifications.WarningLevel, diff --git a/neo4j/record_example_test.go b/neo4j/record_example_test.go index 8499e5bd..84c017b4 100644 --- a/neo4j/record_example_test.go +++ b/neo4j/record_example_test.go @@ -20,7 +20,9 @@ package neo4j_test import ( "context" "fmt" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func ExampleGetRecordValue() { @@ -28,7 +30,7 @@ func ExampleGetRecordValue() { driver, err := createDriver() handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer handleClose(ctx, session) result, err := session.Run(ctx, "MATCH (p:Person) RETURN p LIMIT 1", nil) diff --git a/neo4j/session_with_context.go b/neo4j/session_with_context.go index 2d6322c2..5df0473b 100644 --- a/neo4j/session_with_context.go +++ b/neo4j/session_with_context.go @@ -24,12 +24,12 @@ import ( "time" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/collections" idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/pool" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/telemetry" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/retry" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" @@ -79,107 +79,6 @@ type SessionWithContext interface { verifyAuthentication(ctx context.Context) error } -// SessionConfig is used to configure a new session, its zero value uses safe defaults. -type SessionConfig struct { - // AccessMode used when using Session.Run and explicit transactions. Used to route query - // to read or write servers when running in a cluster. Session.ReadTransaction and Session.WriteTransaction - // does not rely on this mode. - AccessMode AccessMode - // Bookmarks are the initial bookmarks used to ensure that the executing server is at least up - // to date to the point represented by the latest of the provided bookmarks. After running commands - // on the session the bookmark can be retrieved with Session.LastBookmark. All commands executing - // within the same session will automatically use the bookmark from the previous command in the - // session. - Bookmarks bm.Bookmarks - // DatabaseName sets the target database name for the queries executed within the session created with this - // configuration. - // Usage of Cypher clauses like USE is not a replacement for this option. - // Drive​r sends Cypher to the server for processing. - // This option has no explicit value by default, but it is recommended to set one if the target database is known - // in advance. This has the benefit of ensuring a consistent target database name throughout the session in a - // straightforward way and potentially simplifies driver logic as well as reduces network communication resulting - // in better performance. - // When no explicit name is set, the driver behavior depends on the connection URI scheme supplied to the driver on - // instantiation and Bolt protocol version. - // - // Specifically, the following applies: - // - // - for bolt schemes - // queries are dispatched to the server for execution without explicit database name supplied, - // meaning that the target database name for query execution is determined by the server. - // It is important to note that the target database may change (even within the same session), for instance if the - // user's home database is changed on the server. - // - // - for neo4j schemes - // providing that Bolt protocol version 4.4, which was introduced with Neo4j server 4.4, or above - // is available, the driver fetches the user's home database name from the server on first query execution - // within the session and uses the fetched database name explicitly for all queries executed within the session. - // This ensures that the database name remains consistent within the given session. For instance, if the user's - // home database name is 'movies' and the server supplies it to the driver upon database name fetching for the - // session, all queries within that session are executed with the explicit database name 'movies' supplied. - // Any change to the user’s home database is reflected only in sessions created after such change takes effect. - // This behavior requires additional network communication. - // In clustered environments, it is strongly recommended to avoid a single point of failure. - // For instance, by ensuring that the connection URI resolves to multiple endpoints. - // For older Bolt protocol versions, the behavior is the same as described for the bolt schemes above. - DatabaseName string - // FetchSize defines how many records to pull from server in each batch. - // From Bolt protocol v4 (Neo4j 4+) records can be fetched in batches as compared to fetching - // all in previous versions. - // - // If FetchSize is set to FetchDefault, the driver decides the appropriate size. If set to a positive value - // that size is used if the underlying protocol supports it otherwise it is ignored. - // - // To turn off fetching in batches and always fetch everything, set FetchSize to FetchAll. - // If a single large result is to be retrieved this is the most performant setting. - FetchSize int - // Logging target the session will send its Bolt message traces - // - // Possible to use custom logger (implement log.BoltLogger interface) or - // use neo4j.ConsoleBoltLogger. - BoltLogger log.BoltLogger - // ImpersonatedUser sets the Neo4j user that the session will be acting as. - // If not set, the user configured for the driver will be used. - // - // If user impersonation is used, the default database for that impersonated - // user will be used unless DatabaseName is set. - // - // In the former case, when routing is enabled, using impersonation - // without DatabaseName will cause the driver to query the - // cluster for the name of the default database of the impersonated user. - // This is done at the beginning of the session so that queries are routed - // to the correct cluster member (different databases may have different - // leaders). - ImpersonatedUser string - // BookmarkManager defines a central point to externally supply bookmarks - // and be notified of bookmark updates per database - // Since 5.0 - // default: nil (no-op) - BookmarkManager bm.BookmarkManager - // NotificationsMinSeverity defines the minimum severity level of notifications the server should send. - // By default, the driver's settings are used. - // Else, this option overrides the driver's settings. - // Disabling severities allows the server to skip analysis for those, which can speed up query execution. - NotificationsMinSeverity notifications.NotificationMinimumSeverityLevel - // NotificationsDisabledCategories defines the categories of notifications the server should not send. - // By default, the driver's settings are used. - // Else, this option overrides the driver's settings. - // Disabling categories allows the server to skip analysis for those, which can speed up query execution. - NotificationsDisabledCategories notifications.NotificationDisabledCategories - // Auth is used to overwrite the authentication information for the session. - // This requires the server to support re-authentication on the protocol level. - // `nil` will make the driver use the authentication information from the driver configuration. - // The `neo4j` package provides factory functions for common authentication schemes: - // - `neo4j.NoAuth` - // - `neo4j.BasicAuth` - // - `neo4j.KerberosAuth` - // - `neo4j.BearerAuth` - // - `neo4j.CustomAuth` - Auth *AuthToken - - forceReAuth bool -} - // FetchAll turns off fetching records in batches. const FetchAll = -1 @@ -209,13 +108,13 @@ type sessionWithContext struct { log log.Logger throttleTime time.Duration fetchSize int - config SessionConfig + config config.SessionConfig auth *idb.ReAuthToken } func newSessionWithContext( config *Config, - sessConfig SessionConfig, + sessConfig config.SessionConfig, router sessionRouter, pool sessionPool, logger log.Logger, diff --git a/neo4j/session_with_context_test.go b/neo4j/session_with_context_test.go index 71de7b33..b0d5f21b 100644 --- a/neo4j/session_with_context_test.go +++ b/neo4j/session_with_context_test.go @@ -28,6 +28,7 @@ import ( "time" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" idb "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/errorutil" @@ -54,13 +55,13 @@ func TestSession(outer *testing.T) { conf := Config{MaxTransactionRetryTime: 3 * time.Millisecond, MaxConnectionPoolSize: 100} router := RouterFake{} pool := PoolFake{} - sessConfig := SessionConfig{AccessMode: AccessModeRead, BoltLogger: boltLogger} + sessConfig := config.SessionConfig{AccessMode: config.AccessModeRead, BoltLogger: boltLogger} sess := newSessionWithContext(&conf, sessConfig, &router, &pool, logger, nil, &now) sess.throttleTime = time.Millisecond * 1 return &router, &pool, sess } - createSessionFromConfig := func(sessConfig SessionConfig) (*RouterFake, *PoolFake, *sessionWithContext) { + createSessionFromConfig := func(sessConfig config.SessionConfig) (*RouterFake, *PoolFake, *sessionWithContext) { conf := Config{MaxTransactionRetryTime: 3 * time.Millisecond} router := RouterFake{} pool := PoolFake{} @@ -70,7 +71,7 @@ func TestSession(outer *testing.T) { } createSessionWithBookmarks := func(bookmarks bm.Bookmarks) (*RouterFake, *PoolFake, *sessionWithContext) { - sessConfig := SessionConfig{AccessMode: AccessModeRead, Bookmarks: bookmarks, BoltLogger: boltLogger} + sessConfig := config.SessionConfig{AccessMode: config.AccessModeRead, Bookmarks: bookmarks, BoltLogger: boltLogger} return createSessionFromConfig(sessConfig) } @@ -145,7 +146,7 @@ func TestSession(outer *testing.T) { }) inner.Run("Retrieves default database name for impersonated user", func(t *testing.T) { - sessConfig := SessionConfig{ImpersonatedUser: "me"} + sessConfig := config.SessionConfig{ImpersonatedUser: "me"} router, pool, sess := createSessionFromConfig(sessConfig) conn := &ConnFake{} pool.BorrowConn = conn @@ -176,7 +177,7 @@ func TestSession(outer *testing.T) { for name, txFuncApi := range transactionFunctions { inner.Run(fmt.Sprintf("Implicitly rolls back when a %s panics without retry", name), func(t *testing.T) { - _, pool, sess := createSessionFromConfig(SessionConfig{}) + _, pool, sess := createSessionFromConfig(config.SessionConfig{}) pool.BorrowConn = &ConnFake{Alive: true} poolReturnCalled := 0 pool.ReturnHook = func() { @@ -328,7 +329,7 @@ func TestSession(outer *testing.T) { }) inner.Run("Retrieves default database name for impersonated user", func(t *testing.T) { - sessConfig := SessionConfig{ImpersonatedUser: "me"} + sessConfig := config.SessionConfig{ImpersonatedUser: "me"} router, pool, sess := createSessionFromConfig(sessConfig) conn := &ConnFake{} pool.BorrowConn = conn @@ -496,7 +497,7 @@ func TestSession(outer *testing.T) { }) inner.Run("Retrieves default database name for impersonated user", func(t *testing.T) { - sessConfig := SessionConfig{ImpersonatedUser: "me"} + sessConfig := config.SessionConfig{ImpersonatedUser: "me"} router, pool, sess := createSessionFromConfig(sessConfig) conn := &ConnFake{} pool.BorrowConn = conn diff --git a/neo4j/test-integration/auth_test.go b/neo4j/test-integration/auth_test.go index 00473a1a..eb1943b8 100644 --- a/neo4j/test-integration/auth_test.go +++ b/neo4j/test-integration/auth_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -39,7 +40,7 @@ func TestAuthentication(outer *testing.T) { panic(err) } - return driver, driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + return driver, driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) } outer.Run("when wrong credentials are provided, it should fail with authentication error", func(t *testing.T) { diff --git a/neo4j/test-integration/bookmark_test.go b/neo4j/test-integration/bookmark_test.go index 5ab3e57d..7f82bcff 100644 --- a/neo4j/test-integration/bookmark_test.go +++ b/neo4j/test-integration/bookmark_test.go @@ -24,6 +24,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -35,7 +36,7 @@ func TestBookmark(outer *testing.T) { server := dbserver.GetDbServer(ctx) createNodeInTx := func(driver neo4j.DriverWithContext) string { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -59,7 +60,7 @@ func TestBookmark(outer *testing.T) { outer.Run("session constructed with no bookmarks", func(inner *testing.T) { setUp := func() (neo4j.DriverWithContext, neo4j.SessionWithContext) { driver := server.Driver() - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) return driver, session } @@ -212,8 +213,8 @@ func TestBookmark(outer *testing.T) { setUp := func() (neo4j.DriverWithContext, neo4j.SessionWithContext, string) { driver := server.Driver() bookmark := createNodeInTx(driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{ - AccessMode: neo4j.AccessModeWrite, + session := driver.NewSession(ctx, config.SessionConfig{ + AccessMode: config.AccessModeWrite, Bookmarks: bm.BookmarksFromRawValues(bookmark), }) return driver, session, bookmark @@ -312,8 +313,8 @@ func TestBookmark(outer *testing.T) { bookmark2 = createNodeInTx(driver) assertNotEquals(inner, bookmark1, bookmark2) - session = driver.NewSession(ctx, neo4j.SessionConfig{ - AccessMode: neo4j.AccessModeWrite, + session = driver.NewSession(ctx, config.SessionConfig{ + AccessMode: config.AccessModeWrite, Bookmarks: bm.BookmarksFromRawValues(bookmark1, bookmark2), }) @@ -364,8 +365,8 @@ func TestBookmark(outer *testing.T) { bookmark := createNodeInTx(driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{ - AccessMode: neo4j.AccessModeWrite, + session := driver.NewSession(ctx, config.SessionConfig{ + AccessMode: config.AccessModeWrite, Bookmarks: bm.BookmarksFromRawValues(bookmark + "0"), }) return driver, session, bookmark diff --git a/neo4j/test-integration/context_test.go b/neo4j/test-integration/context_test.go index 692ad415..d75b09f6 100644 --- a/neo4j/test-integration/context_test.go +++ b/neo4j/test-integration/context_test.go @@ -19,9 +19,11 @@ package test_integration import ( "context" + "testing" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" - "testing" ) func TestContext(outer *testing.T) { @@ -35,7 +37,7 @@ func TestContext(outer *testing.T) { outer.Run("server does not hold on transaction when driver cancels context", func(t *testing.T) { driver := server.Driver() defer driver.Close(ctx) - session := driver.NewSession(ctx, neo4j.SessionConfig{FetchSize: 1}) + session := driver.NewSession(ctx, config.SessionConfig{FetchSize: 1}) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) assertNil(t, err) @@ -55,7 +57,7 @@ func TestContext(outer *testing.T) { } func listTransactionWorkloads(ctx context.Context, driver neo4j.DriverWithContext, server dbserver.DbServer) []string { - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer session.Close(ctx) transactionQuery := server.GetTransactionWorkloadsQuery() results, err := session.Run(ctx, transactionQuery, nil) diff --git a/neo4j/test-integration/dbserver/dbserver.go b/neo4j/test-integration/dbserver/dbserver.go index ae57c5fc..61caf143 100644 --- a/neo4j/test-integration/dbserver/dbserver.go +++ b/neo4j/test-integration/dbserver/dbserver.go @@ -118,7 +118,7 @@ func setServerVersion(ctx context.Context, server *DbServer, envVersion Version) // this is used when the TEST_NEO4J_VERSION environment variable is not set. func (s *DbServer) getVersionFromDB(ctx context.Context) (Version, error) { driver := s.Driver() - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) result, err := session.Run(ctx, "CALL dbms.components() YIELD versions UNWIND versions AS version RETURN version;", nil) @@ -146,7 +146,7 @@ func (s *DbServer) getVersionFromDB(ctx context.Context) (Version, error) { func (s DbServer) deleteData(ctx context.Context) { driver := s.Driver() - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) for { diff --git a/neo4j/test-integration/driver_test.go b/neo4j/test-integration/driver_test.go index 8fc92332..659c7fa8 100644 --- a/neo4j/test-integration/driver_test.go +++ b/neo4j/test-integration/driver_test.go @@ -19,11 +19,12 @@ package test_integration import ( "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "math" "testing" "time" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -56,7 +57,7 @@ func TestDriver(outer *testing.T) { setUp := func() (neo4j.DriverWithContext, neo4j.SessionWithContext) { driver := server.Driver() - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) return driver, session } @@ -105,7 +106,7 @@ func TestDriver(outer *testing.T) { err = driver.Close(ctx) assertNil(t, err) - _ = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + _ = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) }) }) @@ -130,19 +131,19 @@ func TestDriver(outer *testing.T) { inner.Run("should return error when pool is full", func(t *testing.T) { // Open connection 1 - session1 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session1 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err = session1.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) assertNil(t, err) // Open connection 2 - session2 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session2 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err = session2.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) assertNil(t, err) // Try opening connection 3 - session3 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err = session3.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) assertTrue(t, neo4j.IsConnectivityError(err)) @@ -169,19 +170,19 @@ func TestDriver(outer *testing.T) { inner.Run("should return error when pool is full", func(t *testing.T) { // Open connection 1 - session1 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session1 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err = session1.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) assertNil(t, err) // Open connection 2 - session2 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session2 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err = session2.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) assertNil(t, err) // Try opening connection 3 - session3 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) start := time.Now() _, err = session3.Run(ctx, "UNWIND RANGE(1, 100) AS N RETURN N", nil) diff --git a/neo4j/test-integration/examples_test.go b/neo4j/test-integration/examples_test.go index acddfb67..e7411522 100644 --- a/neo4j/test-integration/examples_test.go +++ b/neo4j/test-integration/examples_test.go @@ -241,7 +241,7 @@ func TestExamples(outer *testing.T) { } // end::geospatial-types-point2d[] - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) assertNotNil(t, session) defer session.Close(ctx) @@ -315,7 +315,7 @@ func TestExamples(outer *testing.T) { } // end::geospatial-types-point3d[] - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) assertNotNil(t, session) defer session.Close(ctx) @@ -379,7 +379,7 @@ func helloWorld(ctx context.Context, uri, username, password string) (string, er } defer driver.Close(ctx) - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) greeting, err := session.ExecuteWrite(ctx, func(transaction neo4j.ManagedTransaction) (any, error) { @@ -476,7 +476,7 @@ func addPerson(ctx context.Context, name string) error { } defer driver.Close(ctx) - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) result, err := session.Run(ctx, "CREATE (n:Person { name: $name})", map[string]any{"name": name}) @@ -526,7 +526,7 @@ func createDriverWithMaxRetryTime(uri, username, password string) (neo4j.DriverW // tag::service-unavailable[] func createItem(ctx context.Context, driver neo4j.DriverWithContext) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -544,7 +544,7 @@ func createItem(ctx context.Context, driver neo4j.DriverWithContext) error { // end::service-unavailable[] func countNodes(ctx context.Context, driver neo4j.DriverWithContext, label string, property string, value string) (int64, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) result, err := session.Run(ctx, fmt.Sprintf("MATCH (a:%s {%s: $value}) RETURN count(a)", label, property), map[string]any{"value": value}) @@ -561,7 +561,7 @@ func countNodes(ctx context.Context, driver neo4j.DriverWithContext, label strin // tag::session[] func addPersonInSession(ctx context.Context, driver neo4j.DriverWithContext, name string) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) result, err := session.Run(ctx, "CREATE (a:Person {name: $name})", map[string]any{"name": name}) @@ -580,7 +580,7 @@ func addPersonInSession(ctx context.Context, driver neo4j.DriverWithContext, nam // tag::autocommit-transaction[] func addPersonInAutoCommitTx(ctx context.Context, driver neo4j.DriverWithContext, name string) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) result, err := session.Run(ctx, "CREATE (a:Person {name: $name})", map[string]any{"name": name}) @@ -599,7 +599,7 @@ func addPersonInAutoCommitTx(ctx context.Context, driver neo4j.DriverWithContext // tag::transaction-function[] func addPersonInTxFunc(ctx context.Context, driver neo4j.DriverWithContext, name string) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -618,7 +618,7 @@ func addPersonInTxFunc(ctx context.Context, driver neo4j.DriverWithContext, name // tag::transaction-timeout-config[] func configTxTimeout(ctx context.Context, driver neo4j.DriverWithContext, name string) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -637,7 +637,7 @@ func configTxTimeout(ctx context.Context, driver neo4j.DriverWithContext, name s // tag::transaction-metadata-config[] func configTxMetadata(ctx context.Context, driver neo4j.DriverWithContext, name string) error { - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -725,7 +725,7 @@ func printFriendsTxFunc(ctx context.Context) neo4j.ManagedTransactionWork { } func addAndEmploy(ctx context.Context, driver neo4j.DriverWithContext, person string, company string) (bm.Bookmarks, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) if _, err := session.ExecuteWrite(ctx, addCompanyTxFunc(ctx, company)); err != nil { @@ -742,7 +742,7 @@ func addAndEmploy(ctx context.Context, driver neo4j.DriverWithContext, person st } func makeFriend(ctx context.Context, driver neo4j.DriverWithContext, person1 string, person2 string, bookmarks bm.Bookmarks) (bm.Bookmarks, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite, Bookmarks: bookmarks}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite, Bookmarks: bookmarks}) defer session.Close(ctx) if _, err := session.ExecuteWrite(ctx, makeFriendTxFunc(ctx, person1, person2)); err != nil { @@ -768,8 +768,8 @@ func addEmployAndMakeFriends(ctx context.Context, driver neo4j.DriverWithContext return err } - session := driver.NewSession(ctx, neo4j.SessionConfig{ - AccessMode: neo4j.AccessModeRead, + session := driver.NewSession(ctx, config.SessionConfig{ + AccessMode: config.AccessModeRead, Bookmarks: bm.CombineBookmarks(bookmarks1, bookmarks2, bookmarks3), }) defer session.Close(ctx) @@ -811,7 +811,7 @@ func matchPersonNodeTxFunc(ctx context.Context, name string) neo4j.ManagedTransa } func addPersonNode(ctx context.Context, driver neo4j.DriverWithContext, name string) (int64, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) if _, err := session.ExecuteWrite(ctx, addPersonNodeTxFunc(ctx, name)); err != nil { @@ -838,14 +838,14 @@ func TestExamplesDatabaseSelection(t *testing.T) { driver := dbserver.GetDbServer(ctx).Driver() defer driver.Close(ctx) // tag::database-selection[] - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "example"}) + session := driver.NewSession(ctx, config.SessionConfig{DatabaseName: "example"}) // end::database-selection[] defer session.Close(ctx) } // tag::result-consume[] func getPeople(ctx context.Context, driver neo4j.DriverWithContext) ([]string, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) people, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -877,7 +877,7 @@ func getPeople(ctx context.Context, driver neo4j.DriverWithContext) ([]string, e // tag::result-retain[] func addPersonsAsEmployees(ctx context.Context, driver neo4j.DriverWithContext, companyName string) (int, error) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) results, err := session.Run(ctx, "MATCH (a:Person) RETURN a.name AS name", nil) diff --git a/neo4j/test-integration/multidatabase_test.go b/neo4j/test-integration/multidatabase_test.go index 38fc1a02..af27c6e5 100644 --- a/neo4j/test-integration/multidatabase_test.go +++ b/neo4j/test-integration/multidatabase_test.go @@ -21,7 +21,7 @@ import ( "context" "testing" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -47,7 +47,7 @@ func TestMultidatabase(outer *testing.T) { // Ensure that a test database exists using system database func() { - sysSess := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "system"}) + sysSess := driver.NewSession(ctx, config.SessionConfig{DatabaseName: "system"}) defer sysSess.Close(ctx) _, err := sysSess.Run(ctx, server.DropDatabaseQuery("testdb"), nil) assertNil(outer, err) @@ -57,17 +57,17 @@ func TestMultidatabase(outer *testing.T) { outer.Run("Node created in test db should not be visible in default db", func(t *testing.T) { // Create node in testdb session - testSess := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "testdb"}) + testSess := driver.NewSession(ctx, config.SessionConfig{DatabaseName: "testdb"}) randId := createRandomNode(ctx, t, testSess) testSess.Close(ctx) // Look for above node in default database session, it shouldn't exist there - defaultSess := driver.NewSession(ctx, neo4j.SessionConfig{}) + defaultSess := driver.NewSession(ctx, config.SessionConfig{}) assertNoRandomNode(ctx, t, defaultSess, randId) defaultSess.Close(ctx) // Look again in testdb session, should of course exist here - testSess = driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "testdb"}) + testSess = driver.NewSession(ctx, config.SessionConfig{DatabaseName: "testdb"}) assertRandomNode(ctx, t, testSess, randId) testSess.Close(ctx) }) diff --git a/neo4j/test-integration/routing_test.go b/neo4j/test-integration/routing_test.go index cbf236a6..44beff94 100644 --- a/neo4j/test-integration/routing_test.go +++ b/neo4j/test-integration/routing_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -55,7 +56,7 @@ func TestRouting(outer *testing.T) { assertNil(t, err) defer driver.Close(ctx) - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) result, err = session.Run(ctx, "RETURN 1", nil) @@ -75,7 +76,7 @@ func TestRouting(outer *testing.T) { driver := getDriver(server.URI()) assertNil(t, err) - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) writeCount, err = session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { writeResult, err := tx.Run(ctx, "MERGE (n:Person {name: 'John'}) RETURN 1", nil) diff --git a/neo4j/test-integration/session_test.go b/neo4j/test-integration/session_test.go index ed8907d4..1bc92164 100644 --- a/neo4j/test-integration/session_test.go +++ b/neo4j/test-integration/session_test.go @@ -20,10 +20,11 @@ package test_integration import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "testing" "time" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" "github.com/neo4j/neo4j-go-driver/v5/neo4j/db" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" @@ -50,7 +51,7 @@ func TestSession(outer *testing.T) { }) assertNotNil(inner, driver) - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) assertNotNil(inner, session) defer func() { @@ -172,7 +173,7 @@ func TestSession(outer *testing.T) { ) driver = server.Driver() - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer func() { if session != nil { @@ -332,7 +333,7 @@ func TestSession(outer *testing.T) { var err error innerExecutor := func() error { - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) if result, err = session.Run(ctx, "UNWIND RANGE(1,100) AS N RETURN N", nil); err != nil { @@ -354,7 +355,7 @@ func TestSession(outer *testing.T) { var err1, err2 error innerExecutor := func() error { - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) if result1, err1 = session.Run(ctx, "UNWIND RANGE(1,100) AS N RETURN N", nil); err != nil { @@ -391,7 +392,7 @@ func TestSession(outer *testing.T) { innerExecutor := func() error { var tx neo4j.ExplicitTransaction - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer session.Close(ctx) if tx, err = session.BeginTransaction(ctx); err != nil { @@ -426,7 +427,7 @@ func TestSession(outer *testing.T) { assertEquals(t, records2[99].Values[0], 100) assertEquals(t, records2[99].Values[1], "Text 100") - newSession := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + newSession := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) assertNotNil(t, newSession) defer newSession.Close(ctx) @@ -449,7 +450,7 @@ func TestSession(outer *testing.T) { inner.Skip("this test is targeted for server version after neo4j 3.5.0") } - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) assertNotNil(inner, session) defer func() { @@ -515,12 +516,12 @@ func TestSession(outer *testing.T) { inner.Run("should set transaction timeout", func(t *testing.T) { createNode(ctx, t, session, "RunTxTimeOut", nil) - session2, tx2 := newSessionAndTx(ctx, t, driver, neo4j.AccessModeWrite) + session2, tx2 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite) defer session2.Close(ctx) defer tx2.Close(ctx) updateNodeInTx(ctx, t, tx2, "RunTxTimeOut", map[string]any{"id": 1}) - session3 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) result3, err := session3.Run(ctx, "MATCH (n:RunTxTimeOut) SET n.id = 2", nil, neo4j.WithTxTimeout(1*time.Second)) // Up to db to determine when error occurs @@ -542,12 +543,12 @@ func TestSession(outer *testing.T) { inner.Run("should set transaction timeout on ExecuteWrite", func(t *testing.T) { createNode(ctx, t, session, "WriteTransactionTxTimeOut", nil) - session2, tx2 := newSessionAndTx(ctx, t, driver, neo4j.AccessModeWrite) + session2, tx2 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite) defer session2.Close(ctx) defer tx2.Close(ctx) updateNodeInTx(ctx, t, tx2, "WriteTransactionTxTimeOut", map[string]any{"id": 1}) - session3 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) _, err := session3.ExecuteWrite(ctx, updateNodeWork(ctx, t, "WriteTransactionTxTimeOut", map[string]any{"id": 2}), neo4j.WithTxTimeout(1*time.Second)) assertNotNil(t, err) diff --git a/neo4j/test-integration/summary_test.go b/neo4j/test-integration/summary_test.go index 17da1834..36ee1d89 100644 --- a/neo4j/test-integration/summary_test.go +++ b/neo4j/test-integration/summary_test.go @@ -52,7 +52,7 @@ func TestResultSummary(outer *testing.T) { } inner.Run("does not include any database information", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, config.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) @@ -72,7 +72,7 @@ func TestResultSummary(outer *testing.T) { inner.Skip("Multi-tenancy is a Neo4j 4+ feature") } - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "system", BoltLogger: neo4j.ConsoleBoltLogger()}) + session := driver.NewSession(ctx, config.SessionConfig{DatabaseName: "system", BoltLogger: neo4j.ConsoleBoltLogger()}) defer assertCloses(ctx, inner, session) res, err := session.Run(ctx, server.CreateDatabaseQuery(extraDatabase), map[string]any{}) assertNil(inner, err) @@ -83,7 +83,7 @@ func TestResultSummary(outer *testing.T) { bookmark = bookmarks[0] defer func() { - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "system", Bookmarks: bm.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, config.SessionConfig{DatabaseName: "system", Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, inner, session) res, err := session.Run(ctx, server.DropDatabaseQuery(extraDatabase), map[string]any{}) assertNil(inner, err) @@ -93,7 +93,7 @@ func TestResultSummary(outer *testing.T) { }() inner.Run("includes the default database information", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, config.SessionConfig{Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) @@ -104,7 +104,7 @@ func TestResultSummary(outer *testing.T) { }) inner.Run("includes the database information, based on session configuration", func(t *testing.T) { - session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: extraDatabase, Bookmarks: bm.BookmarksFromRawValues(bookmark)}) + session := driver.NewSession(ctx, config.SessionConfig{DatabaseName: extraDatabase, Bookmarks: bm.BookmarksFromRawValues(bookmark)}) defer assertCloses(ctx, t, session) result, err := session.Run(ctx, "RETURN 42", noParams) assertNil(t, err) diff --git a/neo4j/test-integration/timeout_test.go b/neo4j/test-integration/timeout_test.go index 7e5b53d1..8a167c7b 100644 --- a/neo4j/test-integration/timeout_test.go +++ b/neo4j/test-integration/timeout_test.go @@ -19,10 +19,11 @@ package test_integration import ( "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "testing" "time" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -49,10 +50,10 @@ func TestTimeoutAndLifetime(outer *testing.T) { assertNotNil(t, driver) defer driver.Close(ctx) - session1, _ = newSessionAndTx(ctx, t, driver, neo4j.AccessModeRead) + session1, _ = newSessionAndTx(ctx, t, driver, config.AccessModeRead) defer session1.Close(ctx) - session2 = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session2 = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) assertNotNil(t, session2) defer session2.Close(ctx) @@ -72,7 +73,7 @@ func TestTimeoutAndLifetime(outer *testing.T) { assertNotNil(t, driver) defer driver.Close(ctx) - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session.Close(ctx) _, err = session.BeginTransaction(ctx) diff --git a/neo4j/test-integration/transaction_test.go b/neo4j/test-integration/transaction_test.go index a84f2d6c..3269837d 100644 --- a/neo4j/test-integration/transaction_test.go +++ b/neo4j/test-integration/transaction_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -41,7 +42,7 @@ func TestTransaction(outer *testing.T) { var result neo4j.ResultWithContext driver = server.Driver() - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) defer func() { if session != nil { @@ -250,7 +251,7 @@ func TestTransaction(outer *testing.T) { t.Skip("Can not list transactions on non-enterprise version") } - session2 := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) + session2 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeRead}) defer session2.Close(ctx) matched, err := session2.ExecuteRead(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata)) assertNil(t, err) @@ -260,13 +261,13 @@ func TestTransaction(outer *testing.T) { inner.Run("should set transaction timeout", func(t *testing.T) { createNode(ctx, t, session, "TxTimeOut", nil) - session2, tx2 := newSessionAndTx(ctx, t, driver, neo4j.AccessModeWrite) + session2, tx2 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite) defer session2.Close(ctx) defer tx2.Close(ctx) updateNodeInTx(ctx, t, tx2, "TxTimeOut", map[string]any{"id": 1}) - session3, tx3 := newSessionAndTx(ctx, t, driver, neo4j.AccessModeWrite, neo4j.WithTxTimeout(1*time.Second)) + session3, tx3 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite, neo4j.WithTxTimeout(1*time.Second)) defer session3.Close(ctx) defer tx3.Close(ctx) diff --git a/neo4j/test-integration/types_test.go b/neo4j/test-integration/types_test.go index 0f656a3b..3b346966 100644 --- a/neo4j/test-integration/types_test.go +++ b/neo4j/test-integration/types_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" ) @@ -41,7 +42,7 @@ func TestTypes(outer *testing.T) { var result neo4j.ResultWithContext driver = server.Driver() - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) assertNotNil(outer, session) defer func() { diff --git a/neo4j/test-integration/utils_test.go b/neo4j/test-integration/utils_test.go index 792342e8..9b5ba9f3 100644 --- a/neo4j/test-integration/utils_test.go +++ b/neo4j/test-integration/utils_test.go @@ -20,11 +20,13 @@ package test_integration import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" "reflect" "sort" "testing" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/test-integration/dbserver" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) @@ -62,8 +64,8 @@ func writeTransactionWithIntWork(ctx context.Context, t *testing.T, session neo4 return result.(int64) } -func newSessionAndTx(ctx context.Context, t *testing.T, driver neo4j.DriverWithContext, mode neo4j.AccessMode, configurers ...func(*neo4j.TransactionConfig)) (neo4j.SessionWithContext, neo4j.ExplicitTransaction) { - session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: mode}) +func newSessionAndTx(ctx context.Context, t *testing.T, driver neo4j.DriverWithContext, mode config.AccessMode, configurers ...func(*neo4j.TransactionConfig)) (neo4j.SessionWithContext, neo4j.ExplicitTransaction) { + session := driver.NewSession(ctx, config.SessionConfig{AccessMode: mode}) tx, err := session.BeginTransaction(ctx, configurers...) assertNil(t, err) diff --git a/neo4j/transaction_helpers_example_test.go b/neo4j/transaction_helpers_example_test.go index 140504b3..78891770 100644 --- a/neo4j/transaction_helpers_example_test.go +++ b/neo4j/transaction_helpers_example_test.go @@ -20,8 +20,10 @@ package neo4j_test import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" "os" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) type Person struct { @@ -45,7 +47,7 @@ func ExampleExecuteWrite() { driver, err := createDriver() handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer handleClose(ctx, session) person := readPersonFromRequest() @@ -84,7 +86,7 @@ func ExampleExecuteRead() { driver, err := createDriver() handleError(err) defer handleClose(ctx, driver) - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) defer handleClose(ctx, session) personLogin := readPersonLoginFromRequest() diff --git a/test-stress/bigdata.go b/test-stress/bigdata.go index 7311dadb..42c6c9d4 100644 --- a/test-stress/bigdata.go +++ b/test-stress/bigdata.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func nCopiesInt(n int, x int) []int { @@ -105,7 +106,7 @@ func runBigDataThing(ctx context.Context, driver neo4j.DriverWithContext) { const nodeCount = 30000 // Write nodes - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) for index := 0; index < nodeCount; { _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { batch := 0 diff --git a/test-stress/clean.go b/test-stress/clean.go index f99fddaf..0910fefb 100644 --- a/test-stress/clean.go +++ b/test-stress/clean.go @@ -19,11 +19,13 @@ package main import ( "context" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func cleanDb(ctx context.Context, driver neo4j.DriverWithContext) { - session := driver.NewSession(ctx, neo4j.SessionConfig{}) + session := driver.NewSession(ctx, config.SessionConfig{}) batch := 1000 for { x, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { diff --git a/test-stress/executors.go b/test-stress/executors.go index 93061796..2c39edcf 100644 --- a/test-stress/executors.go +++ b/test-stress/executors.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) func ExpectNoError(err error) { @@ -60,15 +61,15 @@ func ExpectInt(a, b int) { } } -func newStressSession(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool, accessMode neo4j.AccessMode, testContext *TestContext) neo4j.SessionWithContext { +func newStressSession(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool, accessMode config.AccessMode, testContext *TestContext) neo4j.SessionWithContext { var session neo4j.SessionWithContext if useBookmark { - session = driver.NewSession(ctx, neo4j.SessionConfig{ + session = driver.NewSession(ctx, config.SessionConfig{ AccessMode: accessMode, Bookmarks: testContext.getBookmarks(), }) } else { - session = driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: accessMode}) + session = driver.NewSession(ctx, config.SessionConfig{AccessMode: accessMode}) } ExpectNotNil(session) return session @@ -77,7 +78,7 @@ func newStressSession(ctx context.Context, driver neo4j.DriverWithContext, useBo // ReadQueryExecutor returns a new test executor which reads using Session.Run func ReadQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) result, err := session.Run(ctx, "MATCH (n) RETURN n LIMIT 1", nil) @@ -105,7 +106,7 @@ func ReadQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, useB // ReadQueryInTxExecutor returns a new test executor which reads using Transaction.Run func ReadQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -140,7 +141,7 @@ func ReadQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, // ReadQueryWithReadTransactionExecutor returns a new test executor which reads using Session.ExecuteRead func ReadQueryWithReadTransactionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) summary, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -170,7 +171,7 @@ func ReadQueryWithReadTransactionExecutor(ctx context.Context, driver neo4j.Driv // WriteQueryExecutor returns a new test executor which writes using Session.Run func WriteQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeWrite, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeWrite, testContext) defer session.Close(ctx) result, err := session.Run(ctx, "CREATE ()", nil) @@ -189,7 +190,7 @@ func WriteQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, use // WriteQueryInTxExecutor returns a new test executor which writes using Transaction.Run func WriteQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeWrite, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeWrite, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -215,7 +216,7 @@ func WriteQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, // VaccuumQueryInTxExecutor returns a new test executor which deletes all data func VaccuumQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeWrite, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeWrite, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -240,7 +241,7 @@ func VaccuumQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContex // WriteQueryWithWriteTransactionExecutor returns a new test executor which writes using Session.ExecuteWrite func WriteQueryWithWriteTransactionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeWrite, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeWrite, testContext) defer session.Close(ctx) summary, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -261,7 +262,7 @@ func WriteQueryWithWriteTransactionExecutor(ctx context.Context, driver neo4j.Dr // WriteQueryInReadSessionExecutor returns a new test executor which tries to perform writes using Session.Run with read access mode func WriteQueryInReadSessionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) _, err := session.Run(ctx, "CREATE ()", nil) @@ -272,7 +273,7 @@ func WriteQueryInReadSessionExecutor(ctx context.Context, driver neo4j.DriverWit // WriteQueryInTxInReadSessionExecutor returns a new test executor which tries writes using Transaction.Run with read access mode func WriteQueryInTxInReadSessionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -287,7 +288,7 @@ func WriteQueryInTxInReadSessionExecutor(ctx context.Context, driver neo4j.Drive // FailingQueryExecutor returns a new test executor which fails in streaming using Session.Run func FailingQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) result, err := session.Run(ctx, "UNWIND [10, 5, 0] AS x RETURN 10 / x", nil) @@ -302,7 +303,7 @@ func FailingQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext, u // FailingQueryInTxExecutor returns a new test executor which fails in streaming using Transaction.Run func FailingQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -321,7 +322,7 @@ func FailingQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContex // FailingQueryWithReadTransactionExecutor returns a new test executor which fails in streaming using Session.ExecuteRead func FailingQueryWithReadTransactionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) summary, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -343,7 +344,7 @@ func FailingQueryWithReadTransactionExecutor(ctx context.Context, driver neo4j.D // FailingQueryWithWriteTransactionExecutor returns a new test executor which fails in streaming using Session.ExecuteWrite func FailingQueryWithWriteTransactionExecutor(ctx context.Context, driver neo4j.DriverWithContext, useBookmark bool) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, useBookmark, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, useBookmark, config.AccessModeRead, testContext) defer session.Close(ctx) summary, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { @@ -366,7 +367,7 @@ func FailingQueryWithWriteTransactionExecutor(ctx context.Context, driver neo4j. // WrongQueryExecutor returns a new test executor which fails using Session.Run func WrongQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, false, neo4j.AccessModeRead, testContext) + session := newStressSession(ctx, driver, false, config.AccessModeRead, testContext) defer session.Close(ctx) _, err := session.Run(ctx, "RETURN wrongThing", nil) @@ -377,7 +378,7 @@ func WrongQueryExecutor(ctx context.Context, driver neo4j.DriverWithContext) fun // WrongQueryInTxExecutor returns a new test executor which fails using Transaction.Run func WrongQueryInTxExecutor(ctx context.Context, driver neo4j.DriverWithContext) func(*TestContext) { return func(testContext *TestContext) { - session := newStressSession(ctx, driver, false, neo4j.AccessModeWrite, testContext) + session := newStressSession(ctx, driver, false, config.AccessModeWrite, testContext) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) diff --git a/testkit-backend/backend.go b/testkit-backend/backend.go index b019d64a..1ba0c297 100644 --- a/testkit-backend/backend.go +++ b/testkit-backend/backend.go @@ -620,15 +620,15 @@ func (b *backend) handleRequest(req map[string]any) { case "NewSession": driver := b.drivers[data["driverId"].(string)] - sessionConfig := neo4j.SessionConfig{ + sessionConfig := config.SessionConfig{ BoltLogger: &streamLog{writeLine: b.writeLineLocked}, } if data["accessMode"] != nil { switch data["accessMode"].(string) { case "r": - sessionConfig.AccessMode = neo4j.AccessModeRead + sessionConfig.AccessMode = config.AccessModeRead case "w": - sessionConfig.AccessMode = neo4j.AccessModeWrite + sessionConfig.AccessMode = config.AccessModeWrite default: b.writeError(errors.New("Unknown access mode: " + data["accessMode"].(string))) return @@ -907,7 +907,7 @@ func (b *backend) handleRequest(req map[string]any) { case "CheckMultiDBSupport": driver := b.drivers[data["driverId"].(string)] - session := driver.NewSession(ctx, neo4j.SessionConfig{ + session := driver.NewSession(ctx, config.SessionConfig{ BoltLogger: neo4j.ConsoleBoltLogger(), }) result, err := session.Run(ctx, "RETURN 42", nil) From 606fda20ee75f163a640558154528c4d811b431e Mon Sep 17 00:00:00 2001 From: Luca Pirolo <66700259+lucapirolo@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:28:19 +0000 Subject: [PATCH 3/5] Refactor: Move Session Config Into Dedicated File - Move SessionConfig Into new file named session_config - Move AccessMode and its constants into session_config file - Move AuthToken type alias to session_config file --- neo4j/config/driver.go | 115 ------------------------------- neo4j/config/session_config.go | 121 +++++++++++++++++++++++++++++++++ neo4j/driver_test.go | 2 +- neo4j/driver_with_context.go | 8 --- 4 files changed, 122 insertions(+), 124 deletions(-) create mode 100644 neo4j/config/session_config.go diff --git a/neo4j/config/driver.go b/neo4j/config/driver.go index d622160f..c10eab84 100644 --- a/neo4j/config/driver.go +++ b/neo4j/config/driver.go @@ -22,24 +22,10 @@ import ( "crypto/x509" "time" - bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" - "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/auth" "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" ) -// AccessMode defines modes that routing driver decides to which cluster member -// a connection should be opened. -type AccessMode int -type AuthToken = auth.Token - -const ( - // AccessModeWrite tells the driver to use a connection to 'Leader' - AccessModeWrite AccessMode = 0 - // AccessModeRead tells the driver to use a connection to one of the 'Follower' or 'Read Replica'. - AccessModeRead AccessMode = 1 -) - // A Config contains options that can be used to customize certain // aspects of the driver type Config struct { @@ -189,104 +175,3 @@ type ServerAddress interface { // Port returns the port portion of this ServerAddress. Port() string } - -// SessionConfig is used to configure a new session, its zero value uses safe defaults. -type SessionConfig struct { - // AccessMode used when using Session.Run and explicit transactions. Used to route query - // to read or write servers when running in a cluster. Session.ReadTransaction and Session.WriteTransaction - // does not rely on this mode. - AccessMode AccessMode - // Bookmarks are the initial bookmarks used to ensure that the executing server is at least up - // to date to the point represented by the latest of the provided bookmarks. After running commands - // on the session the bookmark can be retrieved with Session.LastBookmark. All commands executing - // within the same session will automatically use the bookmark from the previous command in the - // session. - Bookmarks bm.Bookmarks - // DatabaseName sets the target database name for the queries executed within the session created with this - // configuration. - // Usage of Cypher clauses like USE is not a replacement for this option. - // Drive​r sends Cypher to the server for processing. - // This option has no explicit value by default, but it is recommended to set one if the target database is known - // in advance. This has the benefit of ensuring a consistent target database name throughout the session in a - // straightforward way and potentially simplifies driver logic as well as reduces network communication resulting - // in better performance. - // When no explicit name is set, the driver behavior depends on the connection URI scheme supplied to the driver on - // instantiation and Bolt protocol version. - // - // Specifically, the following applies: - // - // - for bolt schemes - // queries are dispatched to the server for execution without explicit database name supplied, - // meaning that the target database name for query execution is determined by the server. - // It is important to note that the target database may change (even within the same session), for instance if the - // user's home database is changed on the server. - // - // - for neo4j schemes - // providing that Bolt protocol version 4.4, which was introduced with Neo4j server 4.4, or above - // is available, the driver fetches the user's home database name from the server on first query execution - // within the session and uses the fetched database name explicitly for all queries executed within the session. - // This ensures that the database name remains consistent within the given session. For instance, if the user's - // home database name is 'movies' and the server supplies it to the driver upon database name fetching for the - // session, all queries within that session are executed with the explicit database name 'movies' supplied. - // Any change to the user’s home database is reflected only in sessions created after such change takes effect. - // This behavior requires additional network communication. - // In clustered environments, it is strongly recommended to avoid a single point of failure. - // For instance, by ensuring that the connection URI resolves to multiple endpoints. - // For older Bolt protocol versions, the behavior is the same as described for the bolt schemes above. - DatabaseName string - // FetchSize defines how many records to pull from server in each batch. - // From Bolt protocol v4 (Neo4j 4+) records can be fetched in batches as compared to fetching - // all in previous versions. - // - // If FetchSize is set to FetchDefault, the driver decides the appropriate size. If set to a positive value - // that size is used if the underlying protocol supports it otherwise it is ignored. - // - // To turn off fetching in batches and always fetch everything, set FetchSize to FetchAll. - // If a single large result is to be retrieved this is the most performant setting. - FetchSize int - // Logging target the session will send its Bolt message traces - // - // Possible to use custom logger (implement log.BoltLogger interface) or - // use neo4j.ConsoleBoltLogger. - BoltLogger log.BoltLogger - // ImpersonatedUser sets the Neo4j user that the session will be acting as. - // If not set, the user configured for the driver will be used. - // - // If user impersonation is used, the default database for that impersonated - // user will be used unless DatabaseName is set. - // - // In the former case, when routing is enabled, using impersonation - // without DatabaseName will cause the driver to query the - // cluster for the name of the default database of the impersonated user. - // This is done at the beginning of the session so that queries are routed - // to the correct cluster member (different databases may have different - // leaders). - ImpersonatedUser string - // BookmarkManager defines a central point to externally supply bookmarks - // and be notified of bookmark updates per database - // Since 5.0 - // default: nil (no-op) - BookmarkManager bm.BookmarkManager - // NotificationsMinSeverity defines the minimum severity level of notifications the server should send. - // By default, the driver's settings are used. - // Else, this option overrides the driver's settings. - // Disabling severities allows the server to skip analysis for those, which can speed up query execution. - NotificationsMinSeverity notifications.NotificationMinimumSeverityLevel - // NotificationsDisabledCategories defines the categories of notifications the server should not send. - // By default, the driver's settings are used. - // Else, this option overrides the driver's settings. - // Disabling categories allows the server to skip analysis for those, which can speed up query execution. - NotificationsDisabledCategories notifications.NotificationDisabledCategories - // Auth is used to overwrite the authentication information for the session. - // This requires the server to support re-authentication on the protocol level. - // `nil` will make the driver use the authentication information from the driver configuration. - // The `neo4j` package provides factory functions for common authentication schemes: - // - `neo4j.NoAuth` - // - `neo4j.BasicAuth` - // - `neo4j.KerberosAuth` - // - `neo4j.BearerAuth` - // - `neo4j.CustomAuth` - Auth *AuthToken - - ForceReAuth bool -} diff --git a/neo4j/config/session_config.go b/neo4j/config/session_config.go new file mode 100644 index 00000000..7e5668ff --- /dev/null +++ b/neo4j/config/session_config.go @@ -0,0 +1,121 @@ +package config + +import ( + bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/auth" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" +) + +// AccessMode defines modes that routing driver decides to which cluster member +// a connection should be opened. +type AuthToken = auth.Token +type AccessMode int + +const ( + // AccessModeWrite tells the driver to use a connection to 'Leader' + AccessModeWrite AccessMode = 0 + // AccessModeRead tells the driver to use a connection to one of the 'Follower' or 'Read Replica'. + AccessModeRead AccessMode = 1 +) + +// SessionConfig is used to configure a new session, its zero value uses safe defaults. +type SessionConfig struct { + // AccessMode used when using Session.Run and explicit transactions. Used to route query + // to read or write servers when running in a cluster. Session.ReadTransaction and Session.WriteTransaction + // does not rely on this mode. + AccessMode AccessMode + // Bookmarks are the initial bookmarks used to ensure that the executing server is at least up + // to date to the point represented by the latest of the provided bookmarks. After running commands + // on the session the bookmark can be retrieved with Session.LastBookmark. All commands executing + // within the same session will automatically use the bookmark from the previous command in the + // session. + Bookmarks bm.Bookmarks + // DatabaseName sets the target database name for the queries executed within the session created with this + // configuration. + // Usage of Cypher clauses like USE is not a replacement for this option. + // Drive​r sends Cypher to the server for processing. + // This option has no explicit value by default, but it is recommended to set one if the target database is known + // in advance. This has the benefit of ensuring a consistent target database name throughout the session in a + // straightforward way and potentially simplifies driver logic as well as reduces network communication resulting + // in better performance. + // When no explicit name is set, the driver behavior depends on the connection URI scheme supplied to the driver on + // instantiation and Bolt protocol version. + // + // Specifically, the following applies: + // + // - for bolt schemes + // queries are dispatched to the server for execution without explicit database name supplied, + // meaning that the target database name for query execution is determined by the server. + // It is important to note that the target database may change (even within the same session), for instance if the + // user's home database is changed on the server. + // + // - for neo4j schemes + // providing that Bolt protocol version 4.4, which was introduced with Neo4j server 4.4, or above + // is available, the driver fetches the user's home database name from the server on first query execution + // within the session and uses the fetched database name explicitly for all queries executed within the session. + // This ensures that the database name remains consistent within the given session. For instance, if the user's + // home database name is 'movies' and the server supplies it to the driver upon database name fetching for the + // session, all queries within that session are executed with the explicit database name 'movies' supplied. + // Any change to the user’s home database is reflected only in sessions created after such change takes effect. + // This behavior requires additional network communication. + // In clustered environments, it is strongly recommended to avoid a single point of failure. + // For instance, by ensuring that the connection URI resolves to multiple endpoints. + // For older Bolt protocol versions, the behavior is the same as described for the bolt schemes above. + DatabaseName string + // FetchSize defines how many records to pull from server in each batch. + // From Bolt protocol v4 (Neo4j 4+) records can be fetched in batches as compared to fetching + // all in previous versions. + // + // If FetchSize is set to FetchDefault, the driver decides the appropriate size. If set to a positive value + // that size is used if the underlying protocol supports it otherwise it is ignored. + // + // To turn off fetching in batches and always fetch everything, set FetchSize to FetchAll. + // If a single large result is to be retrieved this is the most performant setting. + FetchSize int + // Logging target the session will send its Bolt message traces + // + // Possible to use custom logger (implement log.BoltLogger interface) or + // use neo4j.ConsoleBoltLogger. + BoltLogger log.BoltLogger + // ImpersonatedUser sets the Neo4j user that the session will be acting as. + // If not set, the user configured for the driver will be used. + // + // If user impersonation is used, the default database for that impersonated + // user will be used unless DatabaseName is set. + // + // In the former case, when routing is enabled, using impersonation + // without DatabaseName will cause the driver to query the + // cluster for the name of the default database of the impersonated user. + // This is done at the beginning of the session so that queries are routed + // to the correct cluster member (different databases may have different + // leaders). + ImpersonatedUser string + // BookmarkManager defines a central point to externally supply bookmarks + // and be notified of bookmark updates per database + // Since 5.0 + // default: nil (no-op) + BookmarkManager bm.BookmarkManager + // NotificationsMinSeverity defines the minimum severity level of notifications the server should send. + // By default, the driver's settings are used. + // Else, this option overrides the driver's settings. + // Disabling severities allows the server to skip analysis for those, which can speed up query execution. + NotificationsMinSeverity notifications.NotificationMinimumSeverityLevel + // NotificationsDisabledCategories defines the categories of notifications the server should not send. + // By default, the driver's settings are used. + // Else, this option overrides the driver's settings. + // Disabling categories allows the server to skip analysis for those, which can speed up query execution. + NotificationsDisabledCategories notifications.NotificationDisabledCategories + // Auth is used to overwrite the authentication information for the session. + // This requires the server to support re-authentication on the protocol level. + // `nil` will make the driver use the authentication information from the driver configuration. + // The `neo4j` package provides factory functions for common authentication schemes: + // - `neo4j.NoAuth` + // - `neo4j.BasicAuth` + // - `neo4j.KerberosAuth` + // - `neo4j.BearerAuth` + // - `neo4j.CustomAuth` + Auth *AuthToken + + ForceReAuth bool +} diff --git a/neo4j/driver_test.go b/neo4j/driver_test.go index a31f3271..ab227783 100644 --- a/neo4j/driver_test.go +++ b/neo4j/driver_test.go @@ -233,7 +233,7 @@ func TestDriverSessionCreation(t *testing.T) { sess := sessi.(*session) if config.AccessMode(sess.delegate.defaultMode) != tt.mode { - t.Errorf("the defaultMode was not correctly set %v", AccessMode(sess.delegate.defaultMode)) + t.Errorf("the defaultMode was not correctly set %v", config.AccessMode(sess.delegate.defaultMode)) } if len(sess.delegate.bookmarks.currentBookmarks()) != len(tt.bookmarks) { diff --git a/neo4j/driver_with_context.go b/neo4j/driver_with_context.go index a14086ab..9bf1d9d3 100644 --- a/neo4j/driver_with_context.go +++ b/neo4j/driver_with_context.go @@ -41,14 +41,6 @@ import ( // AccessMode defines modes that routing driver decides to which cluster member // a connection should be opened. -type AccessMode int - -const ( - // AccessModeWrite tells the driver to use a connection to 'Leader' - AccessModeWrite AccessMode = 0 - // AccessModeRead tells the driver to use a connection to one of the 'Follower' or 'Read Replica'. - AccessModeRead AccessMode = 1 -) // DriverWithContext represents a pool of connections to a neo4j server or cluster. It's // safe for concurrent use. From 50abec003d403915c0ab2f4d8588055cb666d886 Mon Sep 17 00:00:00 2001 From: Luca Pirolo <66700259+lucapirolo@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:47:16 +0000 Subject: [PATCH 4/5] Refactor: Move Transaction Config Into config Package - Relocated all Transaction Config code into the config package and update all references to neo4j.TransactionConfig to config.TransactionConfig --- neo4j/config/transaction_config.go | 85 ++++++++++++++++++++++ neo4j/driver_with_context.go | 2 +- neo4j/driver_with_context_test.go | 12 +-- neo4j/session.go | 25 ++++--- neo4j/session_with_context.go | 46 ++++++------ neo4j/session_with_context_test.go | 2 +- neo4j/test-integration/examples_test.go | 4 +- neo4j/test-integration/session_test.go | 10 +-- neo4j/test-integration/transaction_test.go | 8 +- neo4j/test-integration/types_test.go | 2 +- neo4j/test-integration/utils_test.go | 6 +- neo4j/transaction_config.go | 68 ----------------- neo4j/transaction_helpers.go | 6 +- neo4j/transaction_helpers_test.go | 9 ++- testkit-backend/backend.go | 6 +- 15 files changed, 156 insertions(+), 135 deletions(-) create mode 100644 neo4j/config/transaction_config.go diff --git a/neo4j/config/transaction_config.go b/neo4j/config/transaction_config.go new file mode 100644 index 00000000..df0a0f4b --- /dev/null +++ b/neo4j/config/transaction_config.go @@ -0,0 +1,85 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [https://neo4j.com] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package config + +import "time" + +// TransactionConfig holds the settings for explicit and auto-commit transactions. Actual configuration is expected +// to be done using configuration functions that are predefined, i.e. 'WithTxTimeout' and 'WithTxMetadata', or one +// that you could write by your own. +type TransactionConfig struct { + // Timeout is the configured transaction timeout. + Timeout time.Duration + // Metadata is the configured transaction metadata that will be attached to the underlying transaction. + Metadata map[string]any +} + +// WithTxTimeout returns a transaction configuration function that applies a timeout to a transaction. +// +// Transactions that execute longer than the configured timeout will be terminated by the database. +// This functionality allows user code to limit query/transaction execution time. +// The specified timeout overrides the default timeout configured in the database using the `db.transaction.timeout` +// setting (`dbms.transaction.timeout` before Neo4j 5.0). +// Values higher than `db.transaction.timeout` will be ignored and will fall back to the default for server versions +// between 4.2 and 5.2 (inclusive). +// A `0` duration will make the transaction execute indefinitely. +// `math.MinInt` will use the default timeout configured on the server. +// Other negative durations are invalid. +// +// To apply a transaction timeout to an explicit transaction: +// +// session.BeginTransaction(WithTxTimeout(5*time.Second)) +// +// To apply a transaction timeout to an auto-commit transaction: +// +// session.Run("RETURN 1", nil, WithTxTimeout(5*time.Second)) +// +// To apply a transaction timeout to a read transaction function: +// +// session.ExecuteRead(DoWork, WithTxTimeout(5*time.Second)) +// +// To apply a transaction timeout to a write transaction function: +// +// session.ExecuteWrite(DoWork, WithTxTimeout(5*time.Second)) +func WithTxTimeout(timeout time.Duration) func(*TransactionConfig) { + return func(config *TransactionConfig) { + config.Timeout = timeout + } +} + +// WithTxMetadata returns a transaction configuration function that attaches metadata to a transaction. +// +// To attach a metadata to an explicit transaction: +// +// session.BeginTransaction(WithTxMetadata(map[string)any{"work-id": 1})) +// +// To attach a metadata to an auto-commit transaction: +// +// session.Run("RETURN 1", nil, WithTxMetadata(map[string)any{"work-id": 1})) +// +// To attach a metadata to a read transaction function: +// +// session.ExecuteRead(DoWork, WithTxMetadata(map[string)any{"work-id": 1})) +// +// To attach a metadata to a write transaction function: +// +// session.ExecuteWrite(DoWork, WithTxMetadata(map[string)any{"work-id": 1})) +func WithTxMetadata(metadata map[string]any) func(*TransactionConfig) { + return func(config *TransactionConfig) { + config.Metadata = metadata + } +} diff --git a/neo4j/driver_with_context.go b/neo4j/driver_with_context.go index 9bf1d9d3..0ad7a200 100644 --- a/neo4j/driver_with_context.go +++ b/neo4j/driver_with_context.go @@ -683,7 +683,7 @@ func (c *ExecuteQueryConfiguration) toSessionConfig() config.SessionConfig { } } -type transactionFunction func(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) +type transactionFunction func(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) func (c *ExecuteQueryConfiguration) selectTxFunctionApi(session SessionWithContext) (transactionFunction, error) { switch c.Routing { diff --git a/neo4j/driver_with_context_test.go b/neo4j/driver_with_context_test.go index d81003db..f2092582 100644 --- a/neo4j/driver_with_context_test.go +++ b/neo4j/driver_with_context_test.go @@ -571,18 +571,18 @@ func (s *fakeSession) lastBookmark() string { panic("implement me") } -func (s *fakeSession) BeginTransaction(context.Context, ...func(*TransactionConfig)) (ExplicitTransaction, error) { +func (s *fakeSession) BeginTransaction(context.Context, ...func(*config.TransactionConfig)) (ExplicitTransaction, error) { panic("implement me") } -func (s *fakeSession) ExecuteRead(_ context.Context, callback ManagedTransactionWork, _ ...func(*TransactionConfig)) (any, error) { +func (s *fakeSession) ExecuteRead(_ context.Context, callback ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { return callback(&fakeManagedTransaction{ result: s.executeReadTransactionResult, err: s.executeReadErr, }) } -func (s *fakeSession) ExecuteWrite(_ context.Context, callback ManagedTransactionWork, _ ...func(*TransactionConfig)) (any, error) { +func (s *fakeSession) ExecuteWrite(_ context.Context, callback ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { result := s.executeWriteTransactionResult err := s.executeWriteErr if s.executeWriteErrs != nil { @@ -592,14 +592,14 @@ func (s *fakeSession) ExecuteWrite(_ context.Context, callback ManagedTransactio } return callback(&fakeManagedTransaction{result: result, err: err}) } -func (s *fakeSession) executeQueryRead(_ context.Context, callback ManagedTransactionWork, _ ...func(*TransactionConfig)) (any, error) { +func (s *fakeSession) executeQueryRead(_ context.Context, callback ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { return callback(&fakeManagedTransaction{ result: s.executeReadTransactionResult, err: s.executeReadErr, }) } -func (s *fakeSession) executeQueryWrite(_ context.Context, callback ManagedTransactionWork, _ ...func(*TransactionConfig)) (any, error) { +func (s *fakeSession) executeQueryWrite(_ context.Context, callback ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { result := s.executeWriteTransactionResult err := s.executeWriteErr if s.executeWriteErrs != nil { @@ -609,7 +609,7 @@ func (s *fakeSession) executeQueryWrite(_ context.Context, callback ManagedTrans } return callback(&fakeManagedTransaction{result: result, err: err}) } -func (s *fakeSession) Run(context.Context, string, map[string]any, ...func(*TransactionConfig)) (ResultWithContext, error) { +func (s *fakeSession) Run(context.Context, string, map[string]any, ...func(*config.TransactionConfig)) (ResultWithContext, error) { panic("implement me") } diff --git a/neo4j/session.go b/neo4j/session.go index ded938c2..c6b5e93e 100644 --- a/neo4j/session.go +++ b/neo4j/session.go @@ -21,6 +21,7 @@ import ( "context" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) // Session represents a logical connection (which is not tied to a physical connection) @@ -43,15 +44,15 @@ type Session interface { // Deprecated: since version 5.0. Will be removed in 6.0. Use LastBookmarks instead. LastBookmark() string // BeginTransaction starts a new explicit transaction on this session - BeginTransaction(configurers ...func(*TransactionConfig)) (Transaction, error) + BeginTransaction(configurers ...func(*config.TransactionConfig)) (Transaction, error) // ReadTransaction executes the given unit of work in a AccessModeRead transaction with // retry logic in place - ReadTransaction(work TransactionWork, configurers ...func(*TransactionConfig)) (any, error) + ReadTransaction(work TransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) // WriteTransaction executes the given unit of work in a AccessModeWrite transaction with // retry logic in place - WriteTransaction(work TransactionWork, configurers ...func(*TransactionConfig)) (any, error) + WriteTransaction(work TransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) // Run executes an auto-commit statement and returns a result - Run(cypher string, params map[string]any, configurers ...func(*TransactionConfig)) (Result, error) + Run(cypher string, params map[string]any, configurers ...func(*config.TransactionConfig)) (Result, error) // Close closes any open resources and marks this session as unusable Close() error } @@ -68,7 +69,7 @@ func (s *session) LastBookmark() string { return s.delegate.lastBookmark() } -func (s *session) BeginTransaction(configurers ...func(*TransactionConfig)) (Transaction, error) { +func (s *session) BeginTransaction(configurers ...func(*config.TransactionConfig)) (Transaction, error) { tx, err := s.delegate.BeginTransaction(context.Background(), configurers...) if err != nil { return nil, err @@ -77,7 +78,7 @@ func (s *session) BeginTransaction(configurers ...func(*TransactionConfig)) (Tra } func (s *session) ReadTransaction( - work TransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work TransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.delegate.ExecuteRead( context.Background(), @@ -87,7 +88,7 @@ func (s *session) ReadTransaction( } func (s *session) WriteTransaction( - work TransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work TransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.delegate.ExecuteWrite( context.Background(), @@ -97,7 +98,7 @@ func (s *session) WriteTransaction( } func (s *session) Run( - cypher string, params map[string]any, configurers ...func(*TransactionConfig)) (Result, error) { + cypher string, params map[string]any, configurers ...func(*config.TransactionConfig)) (Result, error) { result, err := s.delegate.Run(context.Background(), cypher, params, configurers...) if err != nil { @@ -128,16 +129,16 @@ func (s *erroredSession) LastBookmarks() bm.Bookmarks { return []string{} } -func (s *erroredSession) BeginTransaction(...func(*TransactionConfig)) (Transaction, error) { +func (s *erroredSession) BeginTransaction(...func(*config.TransactionConfig)) (Transaction, error) { return nil, s.err } -func (s *erroredSession) ReadTransaction(TransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSession) ReadTransaction(TransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSession) WriteTransaction(TransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSession) WriteTransaction(TransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSession) Run(string, map[string]any, ...func(*TransactionConfig)) (Result, error) { +func (s *erroredSession) Run(string, map[string]any, ...func(*config.TransactionConfig)) (Result, error) { return nil, s.err } func (s *erroredSession) Close() error { diff --git a/neo4j/session_with_context.go b/neo4j/session_with_context.go index 5df0473b..d50f3a4f 100644 --- a/neo4j/session_with_context.go +++ b/neo4j/session_with_context.go @@ -57,23 +57,23 @@ type SessionWithContext interface { lastBookmark() string // BeginTransaction starts a new explicit transaction on this session // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. - BeginTransaction(ctx context.Context, configurers ...func(*TransactionConfig)) (ExplicitTransaction, error) + BeginTransaction(ctx context.Context, configurers ...func(*config.TransactionConfig)) (ExplicitTransaction, error) // ExecuteRead executes the given unit of work in a AccessModeRead transaction with // retry logic in place // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. - ExecuteRead(ctx context.Context, work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) + ExecuteRead(ctx context.Context, work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) // ExecuteWrite executes the given unit of work in a AccessModeWrite transaction with // retry logic in place // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. - ExecuteWrite(ctx context.Context, work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) + ExecuteWrite(ctx context.Context, work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) // Run executes an auto-commit statement and returns a result // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. - Run(ctx context.Context, cypher string, params map[string]any, configurers ...func(*TransactionConfig)) (ResultWithContext, error) + Run(ctx context.Context, cypher string, params map[string]any, configurers ...func(*config.TransactionConfig)) (ResultWithContext, error) // Close closes any open resources and marks this session as unusable // Contexts terminating too early negatively affect connection pooling and degrade the driver performance. Close(ctx context.Context) error - executeQueryRead(ctx context.Context, work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) - executeQueryWrite(ctx context.Context, work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) + executeQueryRead(ctx context.Context, work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) + executeQueryWrite(ctx context.Context, work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) legacy() Session getServerInfo(ctx context.Context) (ServerInfo, error) verifyAuthentication(ctx context.Context) error @@ -181,7 +181,7 @@ func (s *sessionWithContext) LastBookmarks() bm.Bookmarks { return s.bookmarks.currentBookmarks() } -func (s *sessionWithContext) BeginTransaction(ctx context.Context, configurers ...func(*TransactionConfig)) (ExplicitTransaction, error) { +func (s *sessionWithContext) BeginTransaction(ctx context.Context, configurers ...func(*config.TransactionConfig)) (ExplicitTransaction, error) { // Guard for more than one transaction per session if s.explicitTx != nil { err := &UsageError{Message: "Session already has a pending transaction"} @@ -263,25 +263,25 @@ func (s *sessionWithContext) BeginTransaction(ctx context.Context, configurers . } func (s *sessionWithContext) ExecuteRead(ctx context.Context, - work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.runRetriable(ctx, idb.ReadMode, work, true, telemetry.ManagedTransaction, configurers...) } func (s *sessionWithContext) ExecuteWrite(ctx context.Context, - work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.runRetriable(ctx, idb.WriteMode, work, true, telemetry.ManagedTransaction, configurers...) } func (s *sessionWithContext) executeQueryRead(ctx context.Context, - work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.runRetriable(ctx, idb.ReadMode, work, false, telemetry.ExecuteQuery, configurers...) } func (s *sessionWithContext) executeQueryWrite(ctx context.Context, - work ManagedTransactionWork, configurers ...func(*TransactionConfig)) (any, error) { + work ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) (any, error) { return s.runRetriable(ctx, idb.WriteMode, work, false, telemetry.ExecuteQuery, configurers...) } @@ -292,7 +292,7 @@ func (s *sessionWithContext) runRetriable( work ManagedTransactionWork, blockingTxBegin bool, api telemetry.API, - configurers ...func(*TransactionConfig)) (any, error) { + configurers ...func(*config.TransactionConfig)) (any, error) { // Guard for more than one transaction per session if s.explicitTx != nil { @@ -337,7 +337,7 @@ func (s *sessionWithContext) runRetriable( func (s *sessionWithContext) executeTransactionFunction( ctx context.Context, mode idb.AccessMode, - config TransactionConfig, + config config.TransactionConfig, state *retry.State, work ManagedTransactionWork, blockingTxBegin bool, @@ -487,7 +487,7 @@ func (s *sessionWithContext) retrieveSessionBookmarks(conn idb.Connection) { } func (s *sessionWithContext) Run(ctx context.Context, - cypher string, params map[string]any, configurers ...func(*TransactionConfig)) (ResultWithContext, error) { + cypher string, params map[string]any, configurers ...func(*config.TransactionConfig)) (ResultWithContext, error) { if s.explicitTx != nil { err := &UsageError{Message: "Trying to run auto-commit transaction while in explicit transaction"} @@ -682,22 +682,22 @@ func (s *erroredSessionWithContext) LastBookmarks() bm.Bookmarks { func (s *erroredSessionWithContext) lastBookmark() string { return "" } -func (s *erroredSessionWithContext) BeginTransaction(context.Context, ...func(*TransactionConfig)) (ExplicitTransaction, error) { +func (s *erroredSessionWithContext) BeginTransaction(context.Context, ...func(*config.TransactionConfig)) (ExplicitTransaction, error) { return nil, s.err } -func (s *erroredSessionWithContext) ExecuteRead(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSessionWithContext) ExecuteRead(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSessionWithContext) ExecuteWrite(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSessionWithContext) ExecuteWrite(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSessionWithContext) executeQueryRead(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSessionWithContext) executeQueryRead(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSessionWithContext) executeQueryWrite(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) { +func (s *erroredSessionWithContext) executeQueryWrite(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) { return nil, s.err } -func (s *erroredSessionWithContext) Run(context.Context, string, map[string]any, ...func(*TransactionConfig)) (ResultWithContext, error) { +func (s *erroredSessionWithContext) Run(context.Context, string, map[string]any, ...func(*config.TransactionConfig)) (ResultWithContext, error) { return nil, s.err } func (s *erroredSessionWithContext) Close(context.Context) error { @@ -714,11 +714,11 @@ func (s *erroredSessionWithContext) verifyAuthentication(context.Context) error return s.err } -func defaultTransactionConfig() TransactionConfig { - return TransactionConfig{Timeout: math.MinInt, Metadata: nil} +func defaultTransactionConfig() config.TransactionConfig { + return config.TransactionConfig{Timeout: math.MinInt, Metadata: nil} } -func validateTransactionConfig(config TransactionConfig) error { +func validateTransactionConfig(config config.TransactionConfig) error { if config.Timeout != math.MinInt && config.Timeout < 0 { err := fmt.Sprintf("Negative transaction timeouts are not allowed. Given: %d", config.Timeout) return &UsageError{Message: err} diff --git a/neo4j/session_with_context_test.go b/neo4j/session_with_context_test.go index b0d5f21b..bc2dcd99 100644 --- a/neo4j/session_with_context_test.go +++ b/neo4j/session_with_context_test.go @@ -37,7 +37,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j/log" ) -type transactionFunc func(context.Context, ManagedTransactionWork, ...func(*TransactionConfig)) (any, error) +type transactionFunc func(context.Context, ManagedTransactionWork, ...func(*config.TransactionConfig)) (any, error) type transactionFuncApi func(session SessionWithContext) transactionFunc func TestSession(outer *testing.T) { diff --git a/neo4j/test-integration/examples_test.go b/neo4j/test-integration/examples_test.go index e7411522..577e7e8a 100644 --- a/neo4j/test-integration/examples_test.go +++ b/neo4j/test-integration/examples_test.go @@ -628,7 +628,7 @@ func configTxTimeout(ctx context.Context, driver neo4j.DriverWithContext, name s } return result.Consume(ctx) - }, neo4j.WithTxTimeout(5*time.Second)) + }, config.WithTxTimeout(5*time.Second)) return err } @@ -647,7 +647,7 @@ func configTxMetadata(ctx context.Context, driver neo4j.DriverWithContext, name } return result.Consume(ctx) - }, neo4j.WithTxMetadata(map[string]any{"applicationId": 123})) + }, config.WithTxMetadata(map[string]any{"applicationId": 123})) return err } diff --git a/neo4j/test-integration/session_test.go b/neo4j/test-integration/session_test.go index 1bc92164..23031679 100644 --- a/neo4j/test-integration/session_test.go +++ b/neo4j/test-integration/session_test.go @@ -475,7 +475,7 @@ func TestSession(outer *testing.T) { t.Skip("Can not list transactions on non-enterprise version") } - matched, err := session.ExecuteRead(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), neo4j.WithTxMetadata(metadata)) + matched, err := session.ExecuteRead(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), config.WithTxMetadata(metadata)) assertNil(t, err) assertTrue(t, matched.(bool)) @@ -492,7 +492,7 @@ func TestSession(outer *testing.T) { t.Skip("Can not list transactions on non-enterprise version") } - matched, err := session.ExecuteRead(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), neo4j.WithTxMetadata(metadata)) + matched, err := session.ExecuteRead(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), config.WithTxMetadata(metadata)) assertNil(t, err) assertTrue(t, matched.(bool)) }) @@ -508,7 +508,7 @@ func TestSession(outer *testing.T) { t.Skip("Can not list transactions on non-enterprise version") } - matched, err := session.ExecuteWrite(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), neo4j.WithTxMetadata(metadata)) + matched, err := session.ExecuteWrite(ctx, listTransactionsAndMatchMetadataWork(ctx, server.Version, metadata), config.WithTxMetadata(metadata)) assertNil(t, err) assertTrue(t, matched.(bool)) }) @@ -523,7 +523,7 @@ func TestSession(outer *testing.T) { session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) - result3, err := session3.Run(ctx, "MATCH (n:RunTxTimeOut) SET n.id = 2", nil, neo4j.WithTxTimeout(1*time.Second)) + result3, err := session3.Run(ctx, "MATCH (n:RunTxTimeOut) SET n.id = 2", nil, config.WithTxTimeout(1*time.Second)) // Up to db to determine when error occurs if err != nil { dbErr := err.(*db.Neo4jError) @@ -550,7 +550,7 @@ func TestSession(outer *testing.T) { session3 := driver.NewSession(ctx, config.SessionConfig{AccessMode: config.AccessModeWrite}) - _, err := session3.ExecuteWrite(ctx, updateNodeWork(ctx, t, "WriteTransactionTxTimeOut", map[string]any{"id": 2}), neo4j.WithTxTimeout(1*time.Second)) + _, err := session3.ExecuteWrite(ctx, updateNodeWork(ctx, t, "WriteTransactionTxTimeOut", map[string]any{"id": 2}), config.WithTxTimeout(1*time.Second)) assertNotNil(t, err) dbErr := err.(*db.Neo4jError) assertStringContains(t, dbErr.Msg, "terminated") diff --git a/neo4j/test-integration/transaction_test.go b/neo4j/test-integration/transaction_test.go index 3269837d..153d8c3a 100644 --- a/neo4j/test-integration/transaction_test.go +++ b/neo4j/test-integration/transaction_test.go @@ -240,7 +240,7 @@ func TestTransaction(outer *testing.T) { "m4": neo4j.LocalDateTimeOf(time.Now()), } - tx, err = session.BeginTransaction(ctx, neo4j.WithTxMetadata(metadata)) + tx, err = session.BeginTransaction(ctx, config.WithTxMetadata(metadata)) assertNil(t, err) defer tx.Close(ctx) @@ -267,7 +267,7 @@ func TestTransaction(outer *testing.T) { updateNodeInTx(ctx, t, tx2, "TxTimeOut", map[string]any{"id": 1}) - session3, tx3 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite, neo4j.WithTxTimeout(1*time.Second)) + session3, tx3 := newSessionAndTx(ctx, t, driver, config.AccessModeWrite, config.WithTxTimeout(1*time.Second)) defer session3.Close(ctx) defer tx3.Close(ctx) @@ -283,12 +283,12 @@ func TestTransaction(outer *testing.T) { } t.Run("should fail when transaction timeout is set for Session.BeginTransaction", func(t *testing.T) { - _, err := session.BeginTransaction(ctx, neo4j.WithTxTimeout(1*time.Second)) + _, err := session.BeginTransaction(ctx, config.WithTxTimeout(1*time.Second)) assertNotNil(t, err) }) t.Run("should fail when transaction metadata is set for Session.BeginTransaction", func(t *testing.T) { - _, err := session.BeginTransaction(ctx, neo4j.WithTxMetadata(map[string]any{"x": 1})) + _, err := session.BeginTransaction(ctx, config.WithTxMetadata(map[string]any{"x": 1})) assertNotNil(t, err) }) }) diff --git a/neo4j/test-integration/types_test.go b/neo4j/test-integration/types_test.go index 3b346966..eb1bbee2 100644 --- a/neo4j/test-integration/types_test.go +++ b/neo4j/test-integration/types_test.go @@ -441,7 +441,7 @@ func TestTypes(outer *testing.T) { }) deepT.Run("should fail when sending as tx metadata", func(t *testing.T) { - result, err = session.Run(ctx, "CREATE (n)", nil, neo4j.WithTxMetadata(map[string]any{"m1": unsupportedType{}})) + result, err = session.Run(ctx, "CREATE (n)", nil, config.WithTxMetadata(map[string]any{"m1": unsupportedType{}})) assertNotNil(t, err) //Expect(err).To(BeGenericError(ContainSubstring("unable to convert tx metadata to connector value for run message"))) assertNil(t, result) diff --git a/neo4j/test-integration/utils_test.go b/neo4j/test-integration/utils_test.go index 9b5ba9f3..c468deea 100644 --- a/neo4j/test-integration/utils_test.go +++ b/neo4j/test-integration/utils_test.go @@ -50,21 +50,21 @@ func transactionWithIntWork(t *testing.T, tx neo4j.ExplicitTransaction, work neo return result.(int64) } -func readTransactionWithIntWork(ctx context.Context, t *testing.T, session neo4j.SessionWithContext, work neo4j.ManagedTransactionWork, configurers ...func(*neo4j.TransactionConfig)) int64 { +func readTransactionWithIntWork(ctx context.Context, t *testing.T, session neo4j.SessionWithContext, work neo4j.ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) int64 { result, err := session.ExecuteRead(ctx, work, configurers...) assertNil(t, err) return result.(int64) } -func writeTransactionWithIntWork(ctx context.Context, t *testing.T, session neo4j.SessionWithContext, work neo4j.ManagedTransactionWork, configurers ...func(*neo4j.TransactionConfig)) int64 { +func writeTransactionWithIntWork(ctx context.Context, t *testing.T, session neo4j.SessionWithContext, work neo4j.ManagedTransactionWork, configurers ...func(*config.TransactionConfig)) int64 { result, err := session.ExecuteWrite(ctx, work, configurers...) assertNil(t, err) return result.(int64) } -func newSessionAndTx(ctx context.Context, t *testing.T, driver neo4j.DriverWithContext, mode config.AccessMode, configurers ...func(*neo4j.TransactionConfig)) (neo4j.SessionWithContext, neo4j.ExplicitTransaction) { +func newSessionAndTx(ctx context.Context, t *testing.T, driver neo4j.DriverWithContext, mode config.AccessMode, configurers ...func(*config.TransactionConfig)) (neo4j.SessionWithContext, neo4j.ExplicitTransaction) { session := driver.NewSession(ctx, config.SessionConfig{AccessMode: mode}) tx, err := session.BeginTransaction(ctx, configurers...) diff --git a/neo4j/transaction_config.go b/neo4j/transaction_config.go index 4f86aae5..dd82e428 100644 --- a/neo4j/transaction_config.go +++ b/neo4j/transaction_config.go @@ -16,71 +16,3 @@ */ package neo4j - -import "time" - -// TransactionConfig holds the settings for explicit and auto-commit transactions. Actual configuration is expected -// to be done using configuration functions that are predefined, i.e. 'WithTxTimeout' and 'WithTxMetadata', or one -// that you could write by your own. -type TransactionConfig struct { - // Timeout is the configured transaction timeout. - Timeout time.Duration - // Metadata is the configured transaction metadata that will be attached to the underlying transaction. - Metadata map[string]any -} - -// WithTxTimeout returns a transaction configuration function that applies a timeout to a transaction. -// -// Transactions that execute longer than the configured timeout will be terminated by the database. -// This functionality allows user code to limit query/transaction execution time. -// The specified timeout overrides the default timeout configured in the database using the `db.transaction.timeout` -// setting (`dbms.transaction.timeout` before Neo4j 5.0). -// Values higher than `db.transaction.timeout` will be ignored and will fall back to the default for server versions -// between 4.2 and 5.2 (inclusive). -// A `0` duration will make the transaction execute indefinitely. -// `math.MinInt` will use the default timeout configured on the server. -// Other negative durations are invalid. -// -// To apply a transaction timeout to an explicit transaction: -// -// session.BeginTransaction(WithTxTimeout(5*time.Second)) -// -// To apply a transaction timeout to an auto-commit transaction: -// -// session.Run("RETURN 1", nil, WithTxTimeout(5*time.Second)) -// -// To apply a transaction timeout to a read transaction function: -// -// session.ExecuteRead(DoWork, WithTxTimeout(5*time.Second)) -// -// To apply a transaction timeout to a write transaction function: -// -// session.ExecuteWrite(DoWork, WithTxTimeout(5*time.Second)) -func WithTxTimeout(timeout time.Duration) func(*TransactionConfig) { - return func(config *TransactionConfig) { - config.Timeout = timeout - } -} - -// WithTxMetadata returns a transaction configuration function that attaches metadata to a transaction. -// -// To attach a metadata to an explicit transaction: -// -// session.BeginTransaction(WithTxMetadata(map[string)any{"work-id": 1})) -// -// To attach a metadata to an auto-commit transaction: -// -// session.Run("RETURN 1", nil, WithTxMetadata(map[string)any{"work-id": 1})) -// -// To attach a metadata to a read transaction function: -// -// session.ExecuteRead(DoWork, WithTxMetadata(map[string)any{"work-id": 1})) -// -// To attach a metadata to a write transaction function: -// -// session.ExecuteWrite(DoWork, WithTxMetadata(map[string)any{"work-id": 1})) -func WithTxMetadata(metadata map[string]any) func(*TransactionConfig) { - return func(config *TransactionConfig) { - config.Metadata = metadata - } -} diff --git a/neo4j/transaction_helpers.go b/neo4j/transaction_helpers.go index 39a01bb2..5706bd21 100644 --- a/neo4j/transaction_helpers.go +++ b/neo4j/transaction_helpers.go @@ -19,6 +19,8 @@ package neo4j import ( "context" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" ) type ManagedTransactionWorkT[T any] func(tx ManagedTransaction) (T, error) @@ -31,7 +33,7 @@ type ManagedTransactionWorkT[T any] func(tx ManagedTransaction) (T, error) // If an error occurs, the zero value of T is returned. func ExecuteRead[T any](ctx context.Context, session SessionWithContext, work ManagedTransactionWorkT[T], - configurers ...func(config *TransactionConfig)) (T, error) { + configurers ...func(config *config.TransactionConfig)) (T, error) { return castGeneric[T](session.ExecuteRead(ctx, transactionWorkAdapter(work), configurers...)) } @@ -44,7 +46,7 @@ func ExecuteRead[T any](ctx context.Context, session SessionWithContext, // If an error occurs, the zero value of T is returned. func ExecuteWrite[T any](ctx context.Context, session SessionWithContext, work ManagedTransactionWorkT[T], - configurers ...func(config *TransactionConfig)) (T, error) { + configurers ...func(config *config.TransactionConfig)) (T, error) { return castGeneric[T](session.ExecuteWrite(ctx, transactionWorkAdapter(work), configurers...)) } diff --git a/neo4j/transaction_helpers_test.go b/neo4j/transaction_helpers_test.go index ba3d180b..30589db8 100644 --- a/neo4j/transaction_helpers_test.go +++ b/neo4j/transaction_helpers_test.go @@ -24,6 +24,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j" bm "github.com/neo4j/neo4j-go-driver/v5/neo4j/bookmarks" + "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" . "github.com/neo4j/neo4j-go-driver/v5/neo4j/internal/testutil" ) @@ -90,19 +91,19 @@ func (f *fakeSession) lastBookmark() string { panic("implement me") } -func (f *fakeSession) BeginTransaction(ctx context.Context, configurers ...func(*neo4j.TransactionConfig)) (neo4j.ExplicitTransaction, error) { +func (f *fakeSession) BeginTransaction(ctx context.Context, configurers ...func(*config.TransactionConfig)) (neo4j.ExplicitTransaction, error) { panic("implement me") } -func (f *fakeSession) ExecuteRead(_ context.Context, work neo4j.ManagedTransactionWork, _ ...func(*neo4j.TransactionConfig)) (any, error) { +func (f *fakeSession) ExecuteRead(_ context.Context, work neo4j.ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { return work(&FakeTransaction{}) } -func (f *fakeSession) ExecuteWrite(_ context.Context, work neo4j.ManagedTransactionWork, _ ...func(*neo4j.TransactionConfig)) (any, error) { +func (f *fakeSession) ExecuteWrite(_ context.Context, work neo4j.ManagedTransactionWork, _ ...func(*config.TransactionConfig)) (any, error) { return work(&FakeTransaction{}) } -func (f *fakeSession) Run(context.Context, string, map[string]any, ...func(*neo4j.TransactionConfig)) (neo4j.ResultWithContext, error) { +func (f *fakeSession) Run(context.Context, string, map[string]any, ...func(*config.TransactionConfig)) (neo4j.ResultWithContext, error) { panic("implement me") } diff --git a/testkit-backend/backend.go b/testkit-backend/backend.go index 1ba0c297..f2170e54 100644 --- a/testkit-backend/backend.go +++ b/testkit-backend/backend.go @@ -296,8 +296,8 @@ func (b *backend) toRequest(s string) map[string]any { return req } -func (b *backend) toTransactionConfigApply(data map[string]any) func(*neo4j.TransactionConfig) { - txConfig := neo4j.TransactionConfig{Timeout: math.MinInt} +func (b *backend) toTransactionConfigApply(data map[string]any) func(*config.TransactionConfig) { + txConfig := config.TransactionConfig{Timeout: math.MinInt} // Optional transaction meta data if data["txMeta"] != nil { txMetadata, err := b.toParams(data["txMeta"].(map[string]any)) @@ -310,7 +310,7 @@ func (b *backend) toTransactionConfigApply(data map[string]any) func(*neo4j.Tran if data["timeout"] != nil { txConfig.Timeout = time.Millisecond * time.Duration(asInt64(data["timeout"].(json.Number))) } - return func(conf *neo4j.TransactionConfig) { + return func(conf *config.TransactionConfig) { if txConfig.Metadata != nil { conf.Metadata = txConfig.Metadata } From 819554d00208544227b71b414771fd7aceac4a8a Mon Sep 17 00:00:00 2001 From: Luca Pirolo <66700259+lucapirolo@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:54:55 +0000 Subject: [PATCH 5/5] Refactor: Add Required Type Aliases to neo4j Package for Backward Compatibility - Added all required type aliases to the Neo4j package to ensure backward compatibility. - This commit introduces SessionConfig, TransactionConfig, and AccessMode aliases in the Neo4j package. --- neo4j/config.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/neo4j/config.go b/neo4j/config.go index 5eb7f2ca..2cb6f89b 100644 --- a/neo4j/config.go +++ b/neo4j/config.go @@ -30,6 +30,15 @@ import ( // Deprecated: please use config.Config directly. This alias will be removed in 6.0. type Config = config.Config +// Deprecated: please use config.SessionConfig directly. This alias will be removed in 6.0. +type SessionConfig = config.SessionConfig + +// Deprecated: please use config.AccessMode directly. This alias will be removed in 6.0. +type AccessMode = config.AccessMode + +// Deprecated: please use config.TransactionConfig directly. This alias will be removed in 6.0. +type TransactionConfig = config.TransactionConfig + // Deprecated: please use bookmarks.Bookmarks directly. This alias will be removed in 6.0. type Bookmarks = bookmarks.Bookmarks