From e3c12afced98c1fa7493907416dace65fb83319b Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Fri, 5 Sep 2025 18:32:41 +0100 Subject: [PATCH] Detect emulated spanner and apply workaround --- storage/gcp/antispam/gcp.go | 23 +++++++++++++++++++++++ storage/gcp/antispam/gcp_test.go | 13 ------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/storage/gcp/antispam/gcp.go b/storage/gcp/antispam/gcp.go index 3727427fe..7c6fd15bd 100644 --- a/storage/gcp/antispam/gcp.go +++ b/storage/gcp/antispam/gcp.go @@ -19,10 +19,15 @@ package gcp import ( + "bytes" + "compress/gzip" "context" + "encoding/base64" "errors" "fmt" + "io" "iter" + "os" "sync/atomic" "time" @@ -191,6 +196,17 @@ func (d *AntispamStorage) Follower(b func([]byte) ([][]byte, error)) tessera.Fol // does not support BatchWrite :( f.updateIndex = f.batchUpdateIndex + if r := os.Getenv("SPANNER_EMULATOR_HOST"); r != "" { + const warn = `H4sIAAAAAAAAA83VwRGAIAwEwH+qoFwrsEAr8eEDPZO7gxkcGV6G7IAJ2tr8iDp07Fs6J7BnImcK5J3EmHVIT2Dvp2YTVJMu/y1+X+jiFQ84LtK9mLHr0aqh+K15PwkWRDaPrcbU5WdMKILtCDMF5hSgQEdJlw/36D7eRYqPfsVNVBcMsNH2QQKq/p957Yr8RfWIE22t7L7ABwAA` + r, _ := base64.StdEncoding.DecodeString(warn) + gzr, _ := gzip.NewReader(bytes.NewReader([]byte(r))) + w, _ := io.ReadAll(gzr) + klog.Warningf("%s\nWarning: you're running under the Spanner emulator - this is not a supported environment!\n\n", string(w)) + + // Hack in a workaround for spannertest not supporting BatchWrites + f.updateIndex = emulatorWorkaroundUpdateIndexTx + } + return f } @@ -460,3 +476,10 @@ func createAndPrepareTables(ctx context.Context, spannerDB string, ddl []string, } return nil } + +// emulatorWorkaroundUpdateIndexTx is a workaround for spannertest not supporting BatchWrites. +// We use this func as a replacement for follower's updateIndex hook, and simply commit the index +// updates inline with the larger transaction. +func emulatorWorkaroundUpdateIndexTx(_ context.Context, txn *spanner.ReadWriteTransaction, ms []*spanner.Mutation) error { + return txn.BufferWrite(ms) +} diff --git a/storage/gcp/antispam/gcp_test.go b/storage/gcp/antispam/gcp_test.go index e7b66f777..29853c25d 100644 --- a/storage/gcp/antispam/gcp_test.go +++ b/storage/gcp/antispam/gcp_test.go @@ -15,13 +15,11 @@ package gcp import ( - "context" "crypto/sha256" "os" "testing" "time" - "cloud.google.com/go/spanner" "cloud.google.com/go/spanner/spannertest" "github.com/transparency-dev/tessera" "github.com/transparency-dev/tessera/api" @@ -78,8 +76,6 @@ func TestAntispamStorage(t *testing.T) { }() f := as.Follower(testBundleHasher) - // Hack in a workaround for spannertest not supporting BatchWrites - f.(*follower).updateIndex = updateIndexTx go f.Follow(t.Context(), fl.LogReader) @@ -168,8 +164,6 @@ func TestAntispamPushbackRecovers(t *testing.T) { }() f := as.Follower(testBundleHasher) - // Hack in a workaround for spannertest not supporting BatchWrites - f.(*follower).updateIndex = updateIndexTx entryIndex := make(map[string]uint64) a := tessera.NewPublicationAwaiter(t.Context(), fl.LogReader.ReadCheckpoint, 100*time.Millisecond) @@ -249,10 +243,3 @@ func testBundleHasher(b []byte) ([][]byte, error) { } return r, err } - -// updateIndexTx is a workaround for spannertest not supporting BatchWrites. -// We use this func as a replacement for follower's updateIndex hook, and simply commit the index -// updates inline with the larger transaction. -func updateIndexTx(_ context.Context, txn *spanner.ReadWriteTransaction, ms []*spanner.Mutation) error { - return txn.BufferWrite(ms) -}