github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/crdb/pool_test.go (about) 1 //go:build docker 2 3 package crdb 4 5 import ( 6 "context" 7 "errors" 8 "testing" 9 "time" 10 11 "github.com/jackc/pgx/v5/pgconn" 12 "github.com/stretchr/testify/require" 13 14 "github.com/authzed/spicedb/internal/datastore/crdb/pool" 15 testdatastore "github.com/authzed/spicedb/internal/testserver/datastore" 16 "github.com/authzed/spicedb/pkg/datastore" 17 "github.com/authzed/spicedb/pkg/namespace" 18 ) 19 20 const ( 21 testUserNamespace = "test/user" 22 ) 23 24 var testUserNS = namespace.Namespace(testUserNamespace) 25 26 func TestTxReset(t *testing.T) { 27 b := testdatastore.RunCRDBForTesting(t, "") 28 29 cases := []struct { 30 name string 31 maxRetries uint8 32 errors []error 33 expectError bool 34 }{ 35 { 36 name: "retryable", 37 maxRetries: 4, 38 errors: []error{ 39 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 40 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 41 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 42 }, 43 expectError: false, 44 }, 45 { 46 name: "resettable", 47 maxRetries: 4, 48 errors: []error{ 49 &pgconn.PgError{Code: pool.CrdbAmbiguousErrorCode}, 50 &pgconn.PgError{Code: pool.CrdbAmbiguousErrorCode}, 51 &pgconn.PgError{Code: pool.CrdbServerNotAcceptingClients}, 52 }, 53 expectError: false, 54 }, 55 { 56 name: "mixed", 57 maxRetries: 50, 58 errors: []error{ 59 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 60 &pgconn.PgError{Code: pool.CrdbAmbiguousErrorCode}, 61 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 62 }, 63 expectError: false, 64 }, 65 { 66 name: "noErrors", 67 maxRetries: 50, 68 errors: []error{}, 69 expectError: false, 70 }, 71 { 72 name: "nonRecoverable", 73 maxRetries: 1, 74 errors: []error{ 75 &pgconn.PgError{Code: pool.CrdbRetryErrCode}, 76 &pgconn.PgError{Code: pool.CrdbAmbiguousErrorCode}, 77 }, 78 expectError: true, 79 }, 80 { 81 name: "stale connections", 82 maxRetries: 3, 83 errors: []error{ 84 errors.New("unexpected EOF"), 85 errors.New("broken pipe"), 86 }, 87 expectError: false, 88 }, 89 { 90 name: "clockSkew", 91 maxRetries: 1, 92 errors: []error{ 93 &pgconn.PgError{Code: pool.CrdbUnknownSQLState, Message: pool.CrdbClockSkewMessage}, 94 }, 95 expectError: false, 96 }, 97 } 98 for _, tt := range cases { 99 tt := tt 100 t.Run(tt.name, func(t *testing.T) { 101 require := require.New(t) 102 ctx := context.Background() 103 104 ds := b.NewDatastore(t, func(engine, uri string) datastore.Datastore { 105 ds, err := newCRDBDatastore( 106 ctx, 107 uri, 108 GCWindow(24*time.Hour), 109 RevisionQuantization(5*time.Second), 110 WatchBufferLength(128), 111 MaxRetries(tt.maxRetries), 112 ) 113 require.NoError(err) 114 return ds 115 }) 116 defer ds.Close() 117 118 r, err := ds.ReadyState(ctx) 119 require.NoError(err) 120 require.True(r.IsReady) 121 122 // WriteNamespace utilizes execute so we'll use it 123 i := 0 124 rev, err := ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error { 125 if i < len(tt.errors) { 126 defer func() { i++ }() 127 return tt.errors[i] 128 } 129 return rwt.WriteNamespaces(ctx, testUserNS) 130 }) 131 if tt.expectError { 132 require.Error(err) 133 require.Equal(datastore.NoRevision, rev) 134 } else { 135 require.NoError(err) 136 require.NotEqual(datastore.NoRevision, rev) 137 } 138 }) 139 } 140 }