Skip to content

Commit 04d02af

Browse files
committed
chore: add cockroachdb 26.1 to the matrix
1 parent acd9e4e commit 04d02af

File tree

9 files changed

+91
-45
lines changed

9 files changed

+91
-45
lines changed

.github/workflows/build-test.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,11 @@ jobs:
262262
fail-fast: false
263263
matrix:
264264
datastore: ["crdb"]
265-
crdbversion: ["25.2.0", "25.3.0"]
265+
crdbversion:
266+
# the version many folks are on
267+
- "25.2.0"
268+
# the most recent version
269+
- "26.1.0"
266270
steps:
267271
- uses: "actions/checkout@v6"
268272
if: |
@@ -297,7 +301,11 @@ jobs:
297301
fail-fast: false
298302
matrix:
299303
datastore: ["crdb"]
300-
crdbversion: ["25.2.0", "25.3.0"]
304+
crdbversion:
305+
# the version many folks are on
306+
- "25.2.0"
307+
# the most recent version
308+
- "26.1.0"
301309
steps:
302310
- uses: "actions/checkout@v6"
303311
if: |

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1414
NOTE: in a future release, MySQL metrics will change.
1515
- Add support for imports and partials to the schemadsl package that drives the LSP and development server (https://github.com/authzed/spicedb/pull/2919).
1616
- `DatastoreTester.New` now takes a `testing.TB` as its first argument, allowing per-test cleanup in datastore test suites (https://github.com/authzed/spicedb/pull/2925).
17+
- Added support for CRDB 26.1 by fixing how version information is read from the cluster
1718

1819
### Fixed
1920
- enforce graceful shutdown on serve and serve-testing (https://github.com/authzed/spicedb/pull/2888)

internal/datastore/crdb/crdb_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"net"
1818
"os"
1919
"path/filepath"
20+
"strconv"
21+
"strings"
2022
"testing"
2123
"time"
2224

@@ -118,7 +120,7 @@ type datastoreTestFunc func(t *testing.T, ds datastore.Datastore)
118120

119121
func createDatastoreTest(b testdatastore.RunningEngineForTest, tf datastoreTestFunc, options ...Option) func(*testing.T) {
120122
return func(t *testing.T) {
121-
ctx := context.Background()
123+
ctx := t.Context()
122124
ds := b.NewDatastore(t, func(engine, uri string) datastore.Datastore {
123125
ds, err := NewCRDBDatastore(ctx, uri, options...)
124126
require.NoError(t, err)
@@ -960,3 +962,39 @@ func TestRegisterPrometheusCollectors(t *testing.T) {
960962
require.NotNil(t, poolReadMetric)
961963
require.Equal(t, float64(readMaxConns), poolReadMetric.GetGauge().GetValue()) //nolint:testifylint // we expect exact values
962964
}
965+
966+
func TestVersionReading(t *testing.T) {
967+
require := require.New(t)
968+
969+
expectedVersionList := strings.Split(crdbTestVersion(), ".")
970+
expectedMajor, err := strconv.Atoi(expectedVersionList[0])
971+
require.NoError(err)
972+
expectedMinor, err := strconv.Atoi(expectedVersionList[1])
973+
require.NoError(err)
974+
expectedPatch, err := strconv.Atoi(expectedVersionList[2])
975+
require.NoError(err)
976+
977+
var version crdbVersion
978+
979+
b := testdatastore.RunCRDBForTesting(t, "", crdbTestVersion())
980+
uri := b.NewDatabase(t)
981+
982+
// Set up a raw connection to the DB
983+
initPoolConfig, err := pgxpool.ParseConfig(uri)
984+
require.NoError(err)
985+
checker, err := pool.NewNodeHealthChecker(uri)
986+
require.NoError(err)
987+
initPool, err := pool.NewRetryPool(t.Context(), "pool", initPoolConfig, checker, 18, 20)
988+
require.NoError(err)
989+
t.Cleanup(func() {
990+
initPool.Close()
991+
})
992+
993+
// Make the query for the server version
994+
err = queryServerVersion(t.Context(), initPool, &version)
995+
require.NoError(err)
996+
997+
require.Equal(expectedMajor, version.Major)
998+
require.Equal(expectedMinor, version.Minor)
999+
require.Equal(expectedPatch, version.Patch)
1000+
}

internal/datastore/crdb/version.go

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,33 @@ package crdb
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"regexp"
87
"strconv"
98

109
"github.com/jackc/pgx/v5"
11-
"github.com/jackc/pgx/v5/pgconn"
1210
"github.com/rs/zerolog"
1311

1412
pgxcommon "github.com/authzed/spicedb/internal/datastore/postgres/common"
1513
)
1614

1715
const (
18-
queryVersionJSON = "SELECT crdb_internal.active_version()::jsonb;"
19-
queryVersion = "SELECT version();"
20-
21-
errFunctionDoesNotExist = "42883"
16+
queryVersion = "SELECT version();"
2217
)
2318

2419
var versionRegex = regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?`)
2520

21+
// queryServerVersion reads the version() string out of CRDB
22+
// and then parses it using a regex to determine the semver.
2623
func queryServerVersion(ctx context.Context, db pgxcommon.DBFuncQuerier, version *crdbVersion) error {
24+
var versionStr string
2725
if err := db.QueryRowFunc(ctx, func(ctx context.Context, row pgx.Row) error {
28-
return row.Scan(version)
29-
}, queryVersionJSON); err != nil {
30-
var pgerr *pgconn.PgError
31-
if !errors.As(err, &pgerr) || pgerr.Code != errFunctionDoesNotExist {
32-
return err
33-
}
34-
35-
// The crdb_internal.active_version() wasn't added until v22.1.X, try to parse the version
36-
var versionStr string
37-
if err := db.QueryRowFunc(ctx, func(ctx context.Context, row pgx.Row) error {
38-
return row.Scan(&versionStr)
39-
}, queryVersion); err != nil {
40-
return err
41-
}
42-
43-
return parseVersionStringInto(versionStr, version)
26+
return row.Scan(&versionStr)
27+
}, queryVersion); err != nil {
28+
return err
4429
}
4530

46-
return nil
31+
return parseVersionStringInto(versionStr, version)
4732
}
4833

4934
func parseVersionStringInto(versionStr string, version *crdbVersion) error {

internal/datastore/crdb/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ const MinimumSupportedCockroachDBVersion = "23.1.30"
88
// LatestTestedCockroachDBVersion is the latest version of CockroachDB that has been tested with this driver.
99
//
1010
// NOTE: must match a tag on DockerHub for the `cockroachdb/cockroach` image, without the `v` prefix.
11-
const LatestTestedCockroachDBVersion = "25.3.2"
11+
const LatestTestedCockroachDBVersion = "26.1.0"

internal/datastore/crdb/watch.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import (
2828
)
2929

3030
const (
31-
queryChangefeed = "CREATE CHANGEFEED FOR %s WITH updated, cursor = '%s', resolved = '%s', min_checkpoint_frequency = '0';"
31+
// cursor: the revision before which we want to start the changefeed
32+
// resolved: the minimum duration between "resolved" checkpoint messages
33+
// min_checkpoint_frequency: how frequently CRDB will attempt to produce a checkpoint. the docs say that this can be 0,
34+
// but that doesn't seem to be true for 26.1, so we set it to 1ms, which is still short but seems to work.
35+
queryChangefeed = "CREATE CHANGEFEED FOR %s WITH updated, cursor = '%s', resolved = '%s', min_checkpoint_frequency = '1ms';"
3236
queryChangefeedPreV25 = "EXPERIMENTAL CHANGEFEED FOR %s WITH updated, cursor = '%s', resolved = '%s', min_checkpoint_frequency = '0';"
3337
queryChangefeedPreV22 = "EXPERIMENTAL CHANGEFEED FOR %s WITH updated, cursor = '%s', resolved = '%s';"
3438
)

internal/datastore/postgres/postgres_shared_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,7 +1937,7 @@ func NullCaveatWatchTest(t *testing.T, ds datastore.Datastore) {
19371937
require.NoError(err)
19381938

19391939
// Verify the relationship create was tracked by the watch.
1940-
test.VerifyUpdates(require, [][]tuple.RelationshipUpdate{
1940+
test.VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
19411941
{
19421942
tuple.Touch(tuple.MustParse("resource:someresourceid#somerelation@subject:somesubject")),
19431943
},
@@ -1953,7 +1953,7 @@ func NullCaveatWatchTest(t *testing.T, ds datastore.Datastore) {
19531953
require.NoError(err)
19541954

19551955
// Verify the delete.
1956-
test.VerifyUpdates(require, [][]tuple.RelationshipUpdate{
1956+
test.VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
19571957
{
19581958
tuple.Delete(tuple.MustParse("resource:someresourceid#somerelation@subject:somesubject")),
19591959
},

pkg/datastore/test/revisions.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020

2121
// RevisionQuantizationTest tests whether or not the requirements for revisions hold
2222
// for a particular datastore.
23-
// TODO: rewrite using synctest
2423
func RevisionQuantizationTest(t *testing.T, tester DatastoreTester) {
2524
testCases := []struct {
2625
quantizationRange time.Duration
@@ -178,6 +177,7 @@ func RevisionGCTest(t *testing.T, tester DatastoreTester) {
178177
time.Sleep(gcWindow)
179178

180179
gcable, ok := ds.(common.GarbageCollectableDatastore)
180+
// NOTE: CRDB and Spanner both do garbage collection with row-level TTLs
181181
if ok {
182182
// Run garbage collection.
183183
gcable.ResetGCCompleted()
@@ -201,6 +201,12 @@ func RevisionGCTest(t *testing.T, tester DatastoreTester) {
201201
head, err = ds.HeadRevision(ctx)
202202
require.NoError(err)
203203

204+
// assert that recent call to head revision is also valid, even after a GC window cycle without writes elapsed
205+
require.NoError(ds.CheckRevision(ctx, head), "expected freshly obtained head revision to be valid")
206+
207+
// TODO: these reads are taking a significant amount of time on CRDB, on the order
208+
// of 100ms for a row read. We need to ascertain whether this is a test artifact
209+
// or a performance regression.
204210
// check that we can read a caveat whose revision has been garbage collectged
205211
_, _, err = ds.SnapshotReader(head).LegacyReadCaveatByName(ctx, testCaveat.Name)
206212
require.NoError(err, "expected previously written caveat should exist at head")
@@ -213,9 +219,6 @@ func RevisionGCTest(t *testing.T, tester DatastoreTester) {
213219
_, _, err = ds.SnapshotReader(head).LegacyReadNamespaceByName(ctx, "foo/bar")
214220
require.NoError(err, "expected previously written schema to exist at head")
215221

216-
// and that recent call to head revision is also valid, even after a GC window cycle without writes elapsed
217-
require.NoError(ds.CheckRevision(ctx, head), "expected freshly obtained head revision to be valid")
218-
219222
// write happens, we get a new head revision
220223
newerRev, err := ds.ReadWriteTx(ctx, func(ctx context.Context, rwt datastore.ReadWriteTransaction) error {
221224
return rwt.LegacyWriteNamespaces(ctx, testNamespace)

pkg/datastore/test/watch.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ func WatchTest(t *testing.T, tester DatastoreTester) {
6969
require.NoError(err)
7070

7171
opts := datastore.WatchOptions{
72-
Content: datastore.WatchRelationships,
73-
WatchBufferLength: 50,
72+
Content: datastore.WatchRelationships,
73+
WatchBufferLength: 50,
74+
// This should mean that we can wait until the checkpoint is emitted and then
75+
// make our writes.
76+
// CheckpointInterval: 1*time.Second,
7477
WatchBufferWriteTimeout: tc.bufferTimeout,
7578
}
7679
changes, errchan := ds.Watch(t.Context(), lowestRevision, opts)
@@ -122,22 +125,24 @@ func WatchTest(t *testing.T, tester DatastoreTester) {
122125
testUpdates = append(testUpdates, bulkDeletes)
123126
}
124127

125-
VerifyUpdates(require, testUpdates, changes, errchan, tc.expectFallBehind)
128+
VerifyUpdates(t, require, testUpdates, changes, errchan, tc.expectFallBehind)
126129

127130
// Test the catch-up case
128131
changes, errchan = ds.Watch(t.Context(), lowestRevision, opts)
129-
VerifyUpdates(require, testUpdates, changes, errchan, tc.expectFallBehind)
132+
VerifyUpdates(t, require, testUpdates, changes, errchan, tc.expectFallBehind)
130133
})
131134
}
132135
}
133136

134137
func VerifyUpdates(
138+
t *testing.T,
135139
require *require.Assertions,
136140
testUpdates [][]tuple.RelationshipUpdate,
137141
changes <-chan datastore.RevisionChanges,
138142
errchan <-chan error,
139143
expectDisconnect bool,
140144
) {
145+
t.Helper()
141146
for _, expected := range testUpdates {
142147
changeWait := time.NewTimer(waitForChangesTimeout)
143148
select {
@@ -174,12 +179,14 @@ func VerifyUpdates(
174179
}
175180

176181
func VerifyUpdatesWithMetadata(
182+
t *testing.T,
177183
require *require.Assertions,
178184
testUpdates []updateWithMetadata,
179185
changes <-chan datastore.RevisionChanges,
180186
errchan <-chan error,
181187
expectDisconnect bool,
182188
) {
189+
t.Helper()
183190
for _, expected := range testUpdates {
184191
changeWait := time.NewTimer(waitForChangesTimeout)
185192
select {
@@ -309,7 +316,7 @@ func WatchWithTouchTest(t *testing.T, tester DatastoreTester) {
309316
tuple.MustParse("document:firstdoc#viewer@user:fred[thirdcaveat]"),
310317
)
311318

312-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
319+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
313320
{
314321
tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom")),
315322
tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:sarah")),
@@ -353,7 +360,7 @@ func WatchWithTouchTest(t *testing.T, tester DatastoreTester) {
353360
tuple.MustParse("document:firstdoc#viewer@user:fred[thirdcaveat]"),
354361
)
355362

356-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
363+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
357364
{tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom[somecaveat]"))},
358365
},
359366
changes,
@@ -374,7 +381,7 @@ func WatchWithTouchTest(t *testing.T, tester DatastoreTester) {
374381
tuple.MustParse("document:firstdoc#viewer@user:fred[thirdcaveat]"),
375382
)
376383

377-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
384+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
378385
{tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom[somecaveat:{\"somecondition\": 42}]"))},
379386
},
380387
changes,
@@ -410,7 +417,7 @@ func WatchWithExpirationTest(t *testing.T, tester DatastoreTester) {
410417
}, options.WithMetadata(metadata))
411418
require.NoError(err)
412419

413-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
420+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
414421
{tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom[expiration:2321-01-01T00:00:00Z]"))},
415422
},
416423
changes,
@@ -455,7 +462,7 @@ func WatchWithMetadataTest(t *testing.T, tester DatastoreTester) {
455462
}, options.WithMetadata(metadata))
456463
require.NoError(err)
457464

458-
VerifyUpdatesWithMetadata(require, []updateWithMetadata{
465+
VerifyUpdatesWithMetadata(t, require, []updateWithMetadata{
459466
{
460467
updates: []tuple.RelationshipUpdate{tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom"))},
461468
metadata: map[string]any{"somekey": "somevalue"},
@@ -498,7 +505,7 @@ func WatchWithDeleteTest(t *testing.T, tester DatastoreTester) {
498505
tuple.MustParse("document:firstdoc#viewer@user:fred[thirdcaveat]"),
499506
)
500507

501-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
508+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
502509
{
503510
tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:tom")),
504511
tuple.Touch(tuple.MustParse("document:firstdoc#viewer@user:sarah")),
@@ -522,7 +529,7 @@ func WatchWithDeleteTest(t *testing.T, tester DatastoreTester) {
522529
tuple.MustParse("document:firstdoc#viewer@user:fred[thirdcaveat]"),
523530
)
524531

525-
VerifyUpdates(require, [][]tuple.RelationshipUpdate{
532+
VerifyUpdates(t, require, [][]tuple.RelationshipUpdate{
526533
{tuple.Delete(tuple.MustParse("document:firstdoc#viewer@user:tom"))},
527534
},
528535
changes,

0 commit comments

Comments
 (0)