From c7dce8139000121ee0223e6d3c88134dd0a9f146 Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Thu, 26 Dec 2024 12:25:00 +0300 Subject: [PATCH] bugfix: schema is reset after a reconnect We need to keep an existing schema resolver to avoid a schema reset after a reconnect. An another approach could be to get a schema on each reconnect, but we will change the expected behavior in this case: load a schema once on a first connect. --- CHANGELOG.md | 3 +++ connection.go | 9 ++++---- tarantool_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f62080c5d..f9ca3519d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. ### Fixed +- `unable to use an index name because schema is not loaded` error after + a reconnect (#424). + ## [v2.2.0] - 2024-12-16 The release introduces the IPROTO_INSERT_ARROW request (arrow.InsertRequest) diff --git a/connection.go b/connection.go index 7c8e1294d..d00b52a57 100644 --- a/connection.go +++ b/connection.go @@ -446,12 +446,13 @@ func (conn *Connection) dial(ctx context.Context) error { conn.Greeting.Salt = connGreeting.Salt conn.serverProtocolInfo = c.ProtocolInfo() - spaceAndIndexNamesSupported := - isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, + if conn.schemaResolver == nil { + namesSupported := isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, conn.serverProtocolInfo.Features) - conn.schemaResolver = &noSchemaResolver{ - SpaceAndIndexNamesSupported: spaceAndIndexNamesSupported, + conn.schemaResolver = &noSchemaResolver{ + SpaceAndIndexNamesSupported: namesSupported, + } } // Watchers. diff --git a/tarantool_test.go b/tarantool_test.go index 98f04a045..e6adebc6d 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -3868,6 +3868,61 @@ func TestWatcher_Unregister_concurrent(t *testing.T) { wg.Wait() } +func TestConnection_named_index_after_reconnect(t *testing.T) { + const server = "127.0.0.1:3015" + + testDialer := dialer + testDialer.Address = server + + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + Dialer: testDialer, + InitScript: "config.lua", + Listen: server, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 10, + RetryTimeout: 500 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + if err != nil { + t.Fatalf("Unable to start Tarantool: %s", err) + } + + reconnectOpts := opts + reconnectOpts.Reconnect = 100 * time.Millisecond + reconnectOpts.MaxReconnects = 10 + + conn := test_helpers.ConnectWithValidation(t, testDialer, reconnectOpts) + defer conn.Close() + + test_helpers.StopTarantool(inst) + + request := NewSelectRequest("test").Index("primary").Limit(1) + _, err = conn.Do(request).Get() + if err == nil { + t.Fatalf("An error expected.") + } + + if err := test_helpers.RestartTarantool(&inst); err != nil { + t.Fatalf("Unable to restart Tarantool: %s", err) + } + + maxTime := reconnectOpts.Reconnect * time.Duration(reconnectOpts.MaxReconnects) + timeout := time.After(maxTime) + + for { + select { + case <-timeout: + t.Fatalf("Failed to execute request without an error, last error: %s", err) + default: + } + + _, err = conn.Do(request).Get() + if err == nil { + return + } + } +} + func TestConnect_schema_update(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close()