tinyrelaycommit

Commit 7d30ccdf656c

browse files at this commit | history

1diff --git a/internal/daemon/git_services.go b/internal/daemon/git_services.go
2index 48a9cd1..42a21bc 100644
3--- a/internal/daemon/git_services.go
4+++ b/internal/daemon/git_services.go
5@@ -154,7 +154,9 @@ func (t *Tenant) gitSyncPeer(ctx context.Context, repo gitrelay.Repository, targ
6 return err
7 }
8 var result error
9+ cursors := make(map[string]struct{})
10 for _, filter := range gitEventFilters(repo) {
11+ cursors[gitHistoryCursorKey(target, filter)] = struct{}{}
12 result = errors.Join(result, t.gitPullFilter(ctx, transport, target, filter))
13 }
14 events, err := t.gitConversationEvents(ctx, repo)
15@@ -162,9 +164,12 @@ func (t *Tenant) gitSyncPeer(ctx context.Context, repo gitrelay.Repository, targ
16 return errors.Join(result, err)
17 }
18 for _, filter := range gitReplyFilters(events) {
19+ cursors[gitHistoryCursorKey(target, filter)] = struct{}{}
20 result = errors.Join(result, t.gitPullFilter(ctx, transport, target, filter))
21 }
22- return result
23+ // Reply filters are chunked by root ID, so their keys shift as roots
24+ // arrive. Drop cursors for filters this pass no longer uses.
25+ return errors.Join(result, t.store.PruneSettings(ctx, gitHistoryCursorPrefix(target), cursors))
26 }
27
28 func (t *Tenant) gitPullFilter(ctx context.Context, transport *replication.NostrTransport, target string, filter event.Filter) error {
29@@ -216,7 +221,7 @@ func (t *Tenant) gitPullLargeFilter(ctx context.Context, transport *replication.
30 if olderErr == nil {
31 // Finished this sweep. The next pass starts another from the head,
32 // allowing older events newly acquired by the peer to be discovered.
33- result = errors.Join(result, t.store.PutSetting(ctx, key, storage.EventCursor{}))
34+ result = errors.Join(result, t.store.DeleteSetting(ctx, key))
35 } else if errors.Is(olderErr, replication.ErrPullIncomplete) && len(older) > 0 {
36 result = errors.Join(result, t.store.PutSetting(ctx, key, oldestGitHistoryCursor(older)))
37 }
38@@ -279,10 +284,12 @@ func oldestGitHistoryCursor(items []event.Event) storage.EventCursor {
39 return storage.EventCursor{CreatedAt: oldest.CreatedAt, ID: oldest.ID}
40 }
41
42+func gitHistoryCursorPrefix(target string) string { return "grasp02.history." + target + "." }
43+
44 func gitHistoryCursorKey(target string, filter event.Filter) string {
45 raw, _ := json.Marshal(filter)
46 sum := sha256.Sum256(raw)
47- return "grasp02.history." + target + "." + hex.EncodeToString(sum[:])
48+ return gitHistoryCursorPrefix(target) + hex.EncodeToString(sum[:])
49 }
50
51 func (t *Tenant) gitHistoryWindow(ctx context.Context, target string, filter event.Filter) (event.Filter, bool, error) {
52diff --git a/internal/daemon/git_sync_cursor_test.go b/internal/daemon/git_sync_cursor_test.go
53index f991e8a..2ecb9de 100644
54--- a/internal/daemon/git_sync_cursor_test.go
55+++ b/internal/daemon/git_sync_cursor_test.go
56@@ -2,6 +2,7 @@ package daemon
57
58 import (
59 "context"
60+ "database/sql"
61 "encoding/json"
62 "errors"
63 "fmt"
64@@ -135,8 +136,11 @@ func TestGitPullFilterAdvancesBeyondBoundedLocalInventory(t *testing.T) {
65 t.Fatalf("older event count = %d, want 1", got)
66 }
67
68- if err := tenant.store.GetSetting(context.Background(), gitHistoryCursorKey("ws://cursor.test", filter), &cursor); err != nil {
69- t.Fatal(err)
70+ // A completed sweep removes its cursor row so per-filter state cannot
71+ // accumulate; the next pass seeds a new head window.
72+ cursor = storage.EventCursor{}
73+ if err := tenant.store.GetSetting(context.Background(), gitHistoryCursorKey("ws://cursor.test", filter), &cursor); !errors.Is(err, sql.ErrNoRows) {
74+ t.Fatalf("completed sweep left cursor %#v, err %v", cursor, err)
75 }
76 before := cursor
77 ctx, cancel := context.WithCancel(context.Background())
78@@ -145,7 +149,7 @@ func TestGitPullFilterAdvancesBeyondBoundedLocalInventory(t *testing.T) {
79 t.Fatal("canceled pull returned nil")
80 }
81 var after storage.EventCursor
82- if err := tenant.store.GetSetting(context.Background(), gitHistoryCursorKey("ws://cursor.test", filter), &after); err != nil {
83+ if err := tenant.store.GetSetting(context.Background(), gitHistoryCursorKey("ws://cursor.test", filter), &after); err != nil && !errors.Is(err, sql.ErrNoRows) {
84 t.Fatal(err)
85 }
86 if after != before {
87diff --git a/internal/storage/maintenance.go b/internal/storage/maintenance.go
88index b36b8a1..325e8c0 100644
89--- a/internal/storage/maintenance.go
90+++ b/internal/storage/maintenance.go
91@@ -114,6 +114,42 @@ func (s *Store) PutSetting(ctx context.Context, key string, value any) error {
92 return s.WithTx(ctx, func(tx *sql.Tx) error { return PutSetting(ctx, tx, key, value) })
93 }
94
95+// DeleteSetting removes one settings row. A missing row is not an error.
96+func (s *Store) DeleteSetting(ctx context.Context, key string) error {
97+ _, err := s.db.ExecContext(ctx, "DELETE FROM settings WHERE key=?", key)
98+ return err
99+}
100+
101+// PruneSettings removes rows under prefix whose keys are not in keep, so
102+// per-filter state cannot accumulate as filters come and go.
103+func (s *Store) PruneSettings(ctx context.Context, prefix string, keep map[string]struct{}) error {
104+ rows, err := s.db.QueryContext(ctx, "SELECT key FROM settings WHERE substr(key,1,?)=?", len(prefix), prefix)
105+ if err != nil {
106+ return err
107+ }
108+ var stale []string
109+ for rows.Next() {
110+ var key string
111+ if err := rows.Scan(&key); err != nil {
112+ rows.Close()
113+ return err
114+ }
115+ if _, ok := keep[key]; !ok {
116+ stale = append(stale, key)
117+ }
118+ }
119+ rows.Close()
120+ if err := rows.Err(); err != nil {
121+ return err
122+ }
123+ for _, key := range stale {
124+ if err := s.DeleteSetting(ctx, key); err != nil {
125+ return err
126+ }
127+ }
128+ return nil
129+}
130+
131 func PutSetting(ctx context.Context, tx *sql.Tx, key string, value any) error {
132 raw, err := json.Marshal(value)
133 if err != nil {

About

ownerc01503e8035f
visibilitypublic
head7d30ccdf656c

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs