carlos / todomvc-bakeoff Public
fix(go): read the cookie key before writing it, so a wake costs no write
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+29
−2
apps/go/app_test.go+20 −0apps/go/store.go+9 −2
diff --git a/apps/go/app_test.go b/apps/go/app_test.go| index 21d61b2..2c5cde9 100644 |
| --- a/apps/go/app_test.go |
| +++ b/apps/go/app_test.go |
| @@ -310,3 +310,23 @@ func TestMigrationsAreRepeatable(t *testing.T) { |
| s.Close() |
| } |
| } |
| + |
| +func TestCookieKeySurvivesRestart(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "k.db") |
| + var keys [][]byte |
| + for range 2 { |
| + s, err := openStore(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + k, err := s.Secret(t.Context()) |
| + if err != nil || len(k) != 32 { |
| + t.Fatalf("key %x, %v", k, err) |
| + } |
| + keys = append(keys, k) |
| + s.Close() |
| + } |
| + if !bytes.Equal(keys[0], keys[1]) { |
| + t.Error("cookie key changed across restarts, so every visitor would lose their list") |
| + } |
| +} |
diff --git a/apps/go/store.go b/apps/go/store.go| index 85354c6..da969d7 100644 |
| --- a/apps/go/store.go |
| +++ b/apps/go/store.go |
| @@ -113,12 +113,19 @@ func (s *Store) Ping(ctx context.Context) error { return s.r.PingContext(ctx) } |
| // Secret returns the instance's cookie-signing key, creating it on first use. |
| func (s *Store) Secret(ctx context.Context) ([]byte, error) { |
| - key := make([]byte, 32) |
| + var key []byte |
| + err := s.r.QueryRowContext(ctx, `SELECT value FROM meta WHERE name = 'cookie_key'`).Scan(&key) |
| + if err == nil { |
| + return key, nil |
| + } else if !errors.Is(err, sql.ErrNoRows) { |
| + return nil, err |
| + } |
| + key = make([]byte, 32) |
| rand.Read(key) |
| if _, err := s.w.ExecContext(ctx, `INSERT INTO meta (name, value) VALUES ('cookie_key', ?) ON CONFLICT DO NOTHING`, key); err != nil { |
| return nil, err |
| } |
| - err := s.w.QueryRowContext(ctx, `SELECT value FROM meta WHERE name = 'cookie_key'`).Scan(&key) |
| + err = s.w.QueryRowContext(ctx, `SELECT value FROM meta WHERE name = 'cookie_key'`).Scan(&key) |
| return key, err |
| } |