From 8105ad13e5cf9a52a0bfe2ddfeb29739e8fb1617 Mon Sep 17 00:00:00 2001 From: Stefano Scafiti Date: Mon, 23 Feb 2026 15:35:56 +0100 Subject: [PATCH 1/5] fix: set timestamp to postgres revision when using optimizedRevisionFunc --- internal/datastore/postgres/postgres.go | 1 + internal/datastore/postgres/revisions.go | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/internal/datastore/postgres/postgres.go b/internal/datastore/postgres/postgres.go index 982a45d45..ba2cc1c70 100644 --- a/internal/datastore/postgres/postgres.go +++ b/internal/datastore/postgres/postgres.go @@ -271,6 +271,7 @@ func newPostgresDatastore( quantizationPeriodNanos, schema.ColSnapshot, followerReadDelayNanos, + schema.ColTimestamp, ) var revisionHeartbeatQuery string diff --git a/internal/datastore/postgres/revisions.go b/internal/datastore/postgres/revisions.go index 18a41f644..4bb34ed46 100644 --- a/internal/datastore/postgres/revisions.go +++ b/internal/datastore/postgres/revisions.go @@ -58,8 +58,9 @@ const ( (SELECT %[1]s FROM %[2]s WHERE %[3]s >= TO_TIMESTAMP(FLOOR((EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc') * 1000000000 - %[6]d)/ %[4]d) * %[4]d / 1000000000) AT TIME ZONE 'utc' ORDER BY %[3]s ASC LIMIT 1) ) as xid) SELECT selected.xid, - COALESCE((SELECT %[5]s FROM %[2]s WHERE %[1]s = selected.xid), (SELECT pg_current_snapshot())), - %[4]d - CAST(EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc') * 1000000000 as bigint) %% %[4]d + COALESCE((SELECT %[5]s FROM %[2]s WHERE %[1]s = selected.xid), (SELECT pg_current_snapshot())), + %[4]d - CAST(EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc') * 1000000000 as bigint) %% %[4]d, + selected.%[1]s AS timestamp FROM selected;` // queryValidTransaction will return a single row with three values: @@ -109,14 +110,25 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re var revision xid8 var snapshot pgSnapshot var validForNanos time.Duration + var timestamp time.Time + if err := pgd.readPool.QueryRow(ctx, pgd.optimizedRevisionQuery). - Scan(&revision, &snapshot, &validForNanos); err != nil { + Scan(&revision, &snapshot, &validForNanos, ×tamp); err != nil { return datastore.NoRevision, 0, fmt.Errorf(errRevision, err) } + tsNanos, err := safecast.Convert[uint64](timestamp.UnixNano()) + if err != nil { + return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") + } + snapshot = snapshot.markComplete(revision.Uint64) - return postgresRevision{snapshot: snapshot, optionalTxID: revision}, validForNanos, nil + return postgresRevision{ + snapshot: snapshot, + optionalInexactNanosTimestamp: tsNanos, + optionalTxID: revision, + }, validForNanos, nil } func (pgd *pgDatastore) HeadRevision(ctx context.Context) (datastore.Revision, error) { From aa07d0b4d795d99dd577fbcf89d6c32ef99a314b Mon Sep 17 00:00:00 2001 From: Stefano Scafiti Date: Mon, 23 Feb 2026 15:47:29 +0100 Subject: [PATCH 2/5] make ts nullable --- internal/datastore/postgres/revisions.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/datastore/postgres/revisions.go b/internal/datastore/postgres/revisions.go index 4bb34ed46..9f0770ad2 100644 --- a/internal/datastore/postgres/revisions.go +++ b/internal/datastore/postgres/revisions.go @@ -110,16 +110,20 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re var revision xid8 var snapshot pgSnapshot var validForNanos time.Duration - var timestamp time.Time + var timestamp *time.Time + var err error if err := pgd.readPool.QueryRow(ctx, pgd.optimizedRevisionQuery). Scan(&revision, &snapshot, &validForNanos, ×tamp); err != nil { return datastore.NoRevision, 0, fmt.Errorf(errRevision, err) } - tsNanos, err := safecast.Convert[uint64](timestamp.UnixNano()) - if err != nil { - return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") + var tsNanos uint64 + if timestamp != nil { + tsNanos, err = safecast.Convert[uint64](timestamp.UnixNano()) + if err != nil { + return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") + } } snapshot = snapshot.markComplete(revision.Uint64) From c1ba30c9bcbaf8f609a262685de17323dcd737fa Mon Sep 17 00:00:00 2001 From: Stefano Scafiti Date: Mon, 23 Feb 2026 15:54:02 +0100 Subject: [PATCH 3/5] fix query --- internal/datastore/postgres/revisions.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/datastore/postgres/revisions.go b/internal/datastore/postgres/revisions.go index 9f0770ad2..858ef6672 100644 --- a/internal/datastore/postgres/revisions.go +++ b/internal/datastore/postgres/revisions.go @@ -60,7 +60,7 @@ const ( SELECT selected.xid, COALESCE((SELECT %[5]s FROM %[2]s WHERE %[1]s = selected.xid), (SELECT pg_current_snapshot())), %[4]d - CAST(EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc') * 1000000000 as bigint) %% %[4]d, - selected.%[1]s AS timestamp + selected.%[3]s AS timestamp FROM selected;` // queryValidTransaction will return a single row with three values: @@ -110,7 +110,7 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re var revision xid8 var snapshot pgSnapshot var validForNanos time.Duration - var timestamp *time.Time + var timestamp time.Time var err error if err := pgd.readPool.QueryRow(ctx, pgd.optimizedRevisionQuery). @@ -118,12 +118,9 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re return datastore.NoRevision, 0, fmt.Errorf(errRevision, err) } - var tsNanos uint64 - if timestamp != nil { - tsNanos, err = safecast.Convert[uint64](timestamp.UnixNano()) - if err != nil { - return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") - } + tsNanos, err := safecast.Convert[uint64](timestamp.UnixNano()) + if err != nil { + return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") } snapshot = snapshot.markComplete(revision.Uint64) From 112fca2d5561d67bf3e0571e2782a4dd071db5a3 Mon Sep 17 00:00:00 2001 From: Stefano Scafiti Date: Mon, 23 Feb 2026 16:02:23 +0100 Subject: [PATCH 4/5] Fix query 2 --- internal/datastore/postgres/revisions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/datastore/postgres/revisions.go b/internal/datastore/postgres/revisions.go index 858ef6672..9c003fa55 100644 --- a/internal/datastore/postgres/revisions.go +++ b/internal/datastore/postgres/revisions.go @@ -60,7 +60,7 @@ const ( SELECT selected.xid, COALESCE((SELECT %[5]s FROM %[2]s WHERE %[1]s = selected.xid), (SELECT pg_current_snapshot())), %[4]d - CAST(EXTRACT(EPOCH FROM NOW() AT TIME ZONE 'utc') * 1000000000 as bigint) %% %[4]d, - selected.%[3]s AS timestamp + (SELECT %[3]s FROM %[2]s WHERE %[1]s = selected.xid) FROM selected;` // queryValidTransaction will return a single row with three values: From e404e01ecd85fa9263d68438d3f187cd5421d499 Mon Sep 17 00:00:00 2001 From: Stefano Scafiti Date: Mon, 23 Feb 2026 16:09:52 +0100 Subject: [PATCH 5/5] ts nullable --- internal/datastore/postgres/revisions.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/datastore/postgres/revisions.go b/internal/datastore/postgres/revisions.go index 9c003fa55..9715b050e 100644 --- a/internal/datastore/postgres/revisions.go +++ b/internal/datastore/postgres/revisions.go @@ -110,7 +110,7 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re var revision xid8 var snapshot pgSnapshot var validForNanos time.Duration - var timestamp time.Time + var timestamp *time.Time var err error if err := pgd.readPool.QueryRow(ctx, pgd.optimizedRevisionQuery). @@ -118,9 +118,12 @@ func (pgd *pgDatastore) optimizedRevisionFunc(ctx context.Context) (datastore.Re return datastore.NoRevision, 0, fmt.Errorf(errRevision, err) } - tsNanos, err := safecast.Convert[uint64](timestamp.UnixNano()) - if err != nil { - return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") + var tsNanos uint64 + if timestamp != nil { + tsNanos, err = safecast.Convert[uint64](timestamp.UnixNano()) + if err != nil { + return nil, 0, spiceerrors.MustBugf("could not cast timestamp to uint64") + } } snapshot = snapshot.markComplete(revision.Uint64)