Skip to content

Commit 3818416

Browse files
committed
Add diff migration
1 parent a7da797 commit 3818416

File tree

7 files changed

+482
-21
lines changed

7 files changed

+482
-21
lines changed

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/ahmetb/govvv v0.3.0
99
github.com/akrylysov/pogreb v0.10.3-0.20240803013244-523613e335e9
1010
github.com/anyproto/any-store v0.3.3
11-
github.com/anyproto/any-sync v0.9.1
11+
github.com/anyproto/any-sync v0.9.3-0.20250804211340-c0752af416a8
1212
github.com/anyproto/go-chash v0.1.0
1313
github.com/cheggaaa/mb/v3 v3.0.2
1414
github.com/planetscale/vtprotobuf v0.6.0
@@ -30,7 +30,7 @@ require (
3030
github.com/anyproto/go-slip10 v1.0.0 // indirect
3131
github.com/anyproto/go-slip21 v1.0.0 // indirect
3232
github.com/anyproto/go-sqlite v1.4.2-any // indirect
33-
github.com/anyproto/lexid v0.0.4 // indirect
33+
github.com/anyproto/lexid v0.0.6 // indirect
3434
github.com/beorn7/perks v1.0.1 // indirect
3535
github.com/btcsuite/btcd v0.22.1 // indirect
3636
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
@@ -47,7 +47,7 @@ require (
4747
github.com/gobwas/glob v0.2.3 // indirect
4848
github.com/goccy/go-graphviz v0.2.9 // indirect
4949
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
50-
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
50+
github.com/golang/snappy v1.0.0 // indirect
5151
github.com/google/uuid v1.6.0 // indirect
5252
github.com/hashicorp/yamux v0.1.2 // indirect
5353
github.com/huandu/skiplist v1.2.1 // indirect
@@ -74,7 +74,7 @@ require (
7474
github.com/prometheus/client_model v0.6.2 // indirect
7575
github.com/prometheus/common v0.64.0 // indirect
7676
github.com/prometheus/procfs v0.16.1 // indirect
77-
github.com/quic-go/quic-go v0.53.0 // indirect
77+
github.com/quic-go/quic-go v0.54.0 // indirect
7878
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
7979
github.com/spaolacci/murmur3 v1.1.0 // indirect
8080
github.com/tetratelabs/wazero v1.8.1 // indirect

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ github.com/akrylysov/pogreb v0.10.3-0.20240803013244-523613e335e9 h1:GnBlbnor8ge
99
github.com/akrylysov/pogreb v0.10.3-0.20240803013244-523613e335e9/go.mod h1:fPb3n+7H42SxX84B4/os7POrR+UGRKSRr3kNS5+xq/c=
1010
github.com/anyproto/any-store v0.3.3 h1:dNzR6YTXt5VaR1aPy4cWlvQ3ozpdO68ABZGA5SfWAPY=
1111
github.com/anyproto/any-store v0.3.3/go.mod h1:337LBJI+JsUsUS1qbKKmtReVhLHW1Mqn4KQux9aEE5A=
12-
github.com/anyproto/any-sync v0.9.1 h1:Jgdn5APJqRWd8k3nuBg7AQ6FzqCmRwWx1q/w+tPrEHU=
13-
github.com/anyproto/any-sync v0.9.1/go.mod h1:WTPTp7QBZEF2eB+q3xRQjM1/atT8gah4KVEjzjTGsbo=
12+
github.com/anyproto/any-sync v0.9.3-0.20250804211340-c0752af416a8 h1:LwsE0rH40WDE+Ir69PMr9SzAGS2bUwFmBiY5tPB4vFE=
13+
github.com/anyproto/any-sync v0.9.3-0.20250804211340-c0752af416a8/go.mod h1:egr+ItXzDs4awlDbNA8UGxWJZWiBnuBA8cUYazAkERk=
1414
github.com/anyproto/go-chash v0.1.0 h1:I9meTPjXFRfXZHRJzjOHC/XF7Q5vzysKkiT/grsogXY=
1515
github.com/anyproto/go-chash v0.1.0/go.mod h1:0UjNQi3PDazP0fINpFYu6VKhuna+W/V+1vpXHAfNgLY=
1616
github.com/anyproto/go-slip10 v1.0.0 h1:uAEtSuudR3jJBOfkOXf3bErxVoxbuKwdoJN55M1i6IA=
@@ -19,8 +19,8 @@ github.com/anyproto/go-slip21 v1.0.0 h1:CI7lUqTIwmPOEGVAj4jyNLoICvueh++0U2HoAi3m
1919
github.com/anyproto/go-slip21 v1.0.0/go.mod h1:gbIJt7HAdr5DuT4f2pFTKCBSUWYsm/fysHBNqgsuxT0=
2020
github.com/anyproto/go-sqlite v1.4.2-any h1:ZTIcq/u2mYYJ6rJB4I3Ds5QH/7IlONebMiG14FyZcD4=
2121
github.com/anyproto/go-sqlite v1.4.2-any/go.mod h1:nW7TvS+4bzPU2vNaPlPFNPYcejK5RdjH6qZY2kjT3Io=
22-
github.com/anyproto/lexid v0.0.4 h1:2ztI0y5pNdtojd3vChw/YV/P6IO9pB7PccYysImDxWI=
23-
github.com/anyproto/lexid v0.0.4/go.mod h1:2RfpYiZkgoNmSDklXdwCCwGlso1FIp9Te8ZtoF3/Ehg=
22+
github.com/anyproto/lexid v0.0.6 h1:lTJd9K11vU1xoBs1dkZVv8VtYfCBo373lNd9kAgXEio=
23+
github.com/anyproto/lexid v0.0.6/go.mod h1:2RfpYiZkgoNmSDklXdwCCwGlso1FIp9Te8ZtoF3/Ehg=
2424
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2525
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
2626
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
@@ -71,8 +71,8 @@ github.com/goccy/go-graphviz v0.2.9/go.mod h1:hssjl/qbvUXGmloY81BwXt2nqoApKo7DFg
7171
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
7272
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
7373
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
74-
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
75-
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
74+
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
75+
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
7676
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
7777
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
7878
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -154,8 +154,8 @@ github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQP
154154
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
155155
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
156156
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
157-
github.com/quic-go/quic-go v0.53.0 h1:QHX46sISpG2S03dPeZBgVIZp8dGagIaiu2FiVYvpCZI=
158-
github.com/quic-go/quic-go v0.53.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
157+
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
158+
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
159159
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
160160
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
161161
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=

nodespace/rpchandler.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func (r *rpcHandler) HeadSync(ctx context.Context, req *spacesyncproto.HeadSyncR
281281
func (r *rpcHandler) tryNodeHeadSync(req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse) {
282282
if len(req.Ranges) == 1 && (req.Ranges[0].From == 0 && req.Ranges[0].To == math.MaxUint64) {
283283
switch req.DiffType {
284-
case spacesyncproto.DiffType_V2:
284+
case spacesyncproto.DiffType_V3:
285285
if req.Ranges[0].Elements {
286286
return nil
287287
}
@@ -295,7 +295,7 @@ func (r *rpcHandler) tryNodeHeadSync(req *spacesyncproto.HeadSyncRequest) (resp
295295
}
296296
log.Debug("got head sync with nodehead", zap.String("spaceId", req.SpaceId))
297297
return &spacesyncproto.HeadSyncResponse{
298-
DiffType: spacesyncproto.DiffType_V2,
298+
DiffType: spacesyncproto.DiffType_V3,
299299
Results: []*spacesyncproto.HeadSyncResult{
300300
{
301301
Hash: hashB,
@@ -315,6 +315,7 @@ func (r *rpcHandler) tryNodeHeadSync(req *spacesyncproto.HeadSyncRequest) (resp
315315
}
316316
log.Debug("got head sync with old nodehead", zap.String("spaceId", req.SpaceId))
317317
return &spacesyncproto.HeadSyncResponse{
318+
DiffType: spacesyncproto.DiffType_V2,
318319
Results: []*spacesyncproto.HeadSyncResult{
319320
{
320321
Hash: hashB,

nodestorage/indexstorage.go

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,29 @@ var (
2626
)
2727

2828
const (
29-
IndexStorageName = ".index"
30-
delCollName = "deletionIndex"
31-
hashCollName = "hashesIndex"
32-
newHashKey = "nh"
33-
oldHashKey = "oh"
34-
statusKey = "s"
35-
recordIdKey = "r"
29+
IndexStorageName = ".index"
30+
delCollName = "deletionIndex"
31+
hashCollName = "hashesIndex"
32+
migrationStateCollName = "migrationState"
33+
newHashKey = "nh"
34+
oldHashKey = "oh"
35+
statusKey = "s"
36+
recordIdKey = "r"
37+
diffMigrationKey = "diffState"
38+
diffVersionKey = "diffVersion"
3639
)
3740

3841
type IndexStorage interface {
3942
UpdateHash(ctx context.Context, update SpaceUpdate) (err error)
4043
RemoveHash(ctx context.Context, spaceId string) (err error)
4144
ReadHashes(ctx context.Context, iterFunc func(update SpaceUpdate) (bool, error)) (err error)
45+
UpdateHashes(ctx context.Context, updateFunc func(spaceId, newHash, oldHash string) (newNewHash, newOldHash string, shouldUpdate bool)) (err error)
4246
SetSpaceStatus(ctx context.Context, spaceId string, status SpaceStatus, recId string) (err error)
4347
SpaceStatus(ctx context.Context, spaceId string) (status SpaceStatus, err error)
4448
LastRecordId(ctx context.Context) (id string, err error)
49+
GetDiffMigrationVersion(ctx context.Context) (version int, err error)
50+
SetDiffMigrationVersion(ctx context.Context, version int) (err error)
51+
RunMigrations(ctx context.Context) (err error)
4552
Close() (err error)
4653
}
4754

@@ -166,6 +173,100 @@ func (d *indexStorage) LastRecordId(ctx context.Context) (id string, err error)
166173
return "", ErrNoLastRecordId
167174
}
168175

176+
func (d *indexStorage) RunMigrations(ctx context.Context) (err error) {
177+
diffMigration, err := newDiffMigration(d, log)
178+
if err != nil {
179+
return fmt.Errorf("failed to create diff migration: %w", err)
180+
}
181+
182+
if err := diffMigration.Run(ctx); err != nil {
183+
return fmt.Errorf("diff migration failed: %w", err)
184+
}
185+
186+
return nil
187+
}
188+
189+
func (d *indexStorage) UpdateHashes(ctx context.Context, updateFunc func(spaceId, newHash, oldHash string) (newNewHash, newOldHash string, shouldUpdate bool)) (err error) {
190+
iter, err := d.hashesColl.Find(query.All{}).Iter(ctx)
191+
if err != nil {
192+
return err
193+
}
194+
defer iter.Close()
195+
196+
tx, err := d.hashesColl.WriteTx(ctx)
197+
if err != nil {
198+
return err
199+
}
200+
defer tx.Rollback()
201+
202+
for iter.Next() {
203+
doc, err := iter.Doc()
204+
if err != nil {
205+
return err
206+
}
207+
208+
value := doc.Value()
209+
spaceId := value.GetString("id")
210+
newHash := value.GetString(newHashKey)
211+
oldHash := value.GetString(oldHashKey)
212+
213+
newNewHash, newOldHash, shouldUpdate := updateFunc(spaceId, newHash, oldHash)
214+
if !shouldUpdate {
215+
continue
216+
}
217+
218+
arena := d.arenaPool.Get()
219+
updatedDoc := arena.NewObject()
220+
updatedDoc.Set("id", arena.NewString(spaceId))
221+
updatedDoc.Set(newHashKey, arena.NewString(newNewHash))
222+
updatedDoc.Set(oldHashKey, arena.NewString(newOldHash))
223+
224+
err = d.hashesColl.UpsertOne(tx.Context(), updatedDoc)
225+
d.arenaPool.Put(arena)
226+
if err != nil {
227+
return err
228+
}
229+
}
230+
231+
return tx.Commit()
232+
}
233+
234+
func (d *indexStorage) GetDiffMigrationVersion(ctx context.Context) (version int, err error) {
235+
migrationColl, err := d.db.Collection(ctx, migrationStateCollName)
236+
if err != nil {
237+
return 0, err
238+
}
239+
240+
doc, err := migrationColl.FindId(ctx, diffMigrationKey)
241+
if err != nil {
242+
if errors.Is(err, anystore.ErrDocNotFound) {
243+
return 0, nil
244+
}
245+
return 0, err
246+
}
247+
248+
return int(doc.Value().GetFloat64(diffVersionKey)), nil
249+
}
250+
251+
func (d *indexStorage) SetDiffMigrationVersion(ctx context.Context, version int) (err error) {
252+
migrationColl, err := d.db.Collection(ctx, migrationStateCollName)
253+
if err != nil {
254+
return err
255+
}
256+
257+
mod := query.ModifyFunc(func(a *anyenc.Arena, v *anyenc.Value) (result *anyenc.Value, modified bool, err error) {
258+
if v == nil {
259+
v = a.NewObject()
260+
v.Set("id", a.NewString(diffMigrationKey))
261+
}
262+
v.Set(diffVersionKey, a.NewNumberFloat64(float64(version)))
263+
return v, true, nil
264+
})
265+
266+
_, err = migrationColl.UpsertId(ctx, diffMigrationKey, mod)
267+
return err
268+
}
269+
169270
func (d *indexStorage) Close() (err error) {
170271
return d.db.Close()
171272
}

nodestorage/migration.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package nodestorage
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/anyproto/any-sync/app/logger"
8+
"go.uber.org/zap"
9+
)
10+
11+
const (
12+
targetMigrationVersion = 3
13+
)
14+
15+
type Migration interface {
16+
Run(ctx context.Context) error
17+
}
18+
19+
type diffMigration struct {
20+
indexStorage IndexStorage
21+
logger logger.CtxLogger
22+
}
23+
24+
func newDiffMigration(is IndexStorage, log logger.CtxLogger) (*diffMigration, error) {
25+
return &diffMigration{
26+
indexStorage: is,
27+
logger: log,
28+
}, nil
29+
}
30+
31+
func (m *diffMigration) Run(ctx context.Context) error {
32+
m.logger.Info("starting diff migration v2 to v3")
33+
34+
currentVersion, err := m.indexStorage.GetDiffMigrationVersion(ctx)
35+
if err != nil {
36+
return fmt.Errorf("failed to get migration version: %w", err)
37+
}
38+
39+
if currentVersion >= targetMigrationVersion {
40+
m.logger.Info("diff migration already completed", zap.Int("version", currentVersion))
41+
return nil
42+
}
43+
44+
err = m.migrateHashIndex(ctx)
45+
if err != nil {
46+
return fmt.Errorf("failed to migrate hash index: %w", err)
47+
}
48+
49+
err = m.indexStorage.SetDiffMigrationVersion(ctx, targetMigrationVersion)
50+
if err != nil {
51+
return fmt.Errorf("failed to set migration version: %w", err)
52+
}
53+
54+
m.logger.Info("diff migration completed successfully")
55+
return nil
56+
}
57+
58+
func (m *diffMigration) migrateHashIndex(ctx context.Context) error {
59+
migratedCount := 0
60+
61+
err := m.indexStorage.UpdateHashes(ctx, func(spaceId, newHash, oldHash string) (newNewHash, newOldHash string, shouldUpdate bool) {
62+
migratedCount++
63+
return newHash, newHash, true
64+
})
65+
66+
if err != nil {
67+
return fmt.Errorf("failed to update hashes: %w", err)
68+
}
69+
70+
m.logger.Info("hash index migration completed", zap.Int("migrated_count", migratedCount))
71+
return nil
72+
}

0 commit comments

Comments
 (0)