Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee

fix: one-time seeding of sqlite feed db

pdewey.com 56d28489 5d903745

verified
+47
+11
cmd/server/main.go
··· 176 177 log.Info().Str("path", feedIndexPath).Msg("Feed index opened") 178 179 // Populate feed registry from SQLite known_dids (replaces BoltDB feed registry) 180 if knownDIDs, err := feedIndex.GetKnownDIDs(); err == nil { 181 for _, did := range knownDIDs {
··· 176 177 log.Info().Str("path", feedIndexPath).Msg("Feed index opened") 178 179 + // One-time seed: copy any DIDs from the legacy BoltDB feed_registry bucket into 180 + // SQLite known_dids. INSERT OR IGNORE makes this a no-op once DIDs are present. 181 + if legacyDIDs := store.LegacyFeedDIDs(); len(legacyDIDs) > 0 { 182 + added, err := feedIndex.SeedKnownDIDs(legacyDIDs) 183 + if err != nil { 184 + log.Warn().Err(err).Msg("Failed to seed known DIDs from legacy feed registry") 185 + } else if added > 0 { 186 + log.Info().Int("seeded", added).Msg("Seeded known_dids from legacy BoltDB feed registry") 187 + } 188 + } 189 + 190 // Populate feed registry from SQLite known_dids (replaces BoltDB feed registry) 191 if knownDIDs, err := feedIndex.GetKnownDIDs(); err == nil { 192 for _, did := range knownDIDs {
+18
internal/database/boltstore/store.go
··· 129 return &JoinStore{db: s.db} 130 } 131 132 // Stats returns database statistics. 133 func (s *Store) Stats() bolt.Stats { 134 return s.db.Stats()
··· 129 return &JoinStore{db: s.db} 130 } 131 132 + // LegacyFeedDIDs reads DIDs from the old feed_registry bucket that existed 133 + // before the feed registry was moved to SQLite. Used for one-time migration 134 + // seeding on first startup after the transition. 135 + func (s *Store) LegacyFeedDIDs() []string { 136 + var dids []string 137 + _ = s.db.View(func(tx *bolt.Tx) error { 138 + bucket := tx.Bucket([]byte("feed_registry")) 139 + if bucket == nil { 140 + return nil 141 + } 142 + return bucket.ForEach(func(k, _ []byte) error { 143 + dids = append(dids, string(k)) 144 + return nil 145 + }) 146 + }) 147 + return dids 148 + } 149 + 150 // Stats returns database statistics. 151 func (s *Store) Stats() bolt.Stats { 152 return s.db.Stats()
+18
internal/firehose/index.go
··· 245 return idx.db 246 } 247 248 // Close closes the index database 249 func (idx *FeedIndex) Close() error { 250 if idx.db != nil {
··· 245 return idx.db 246 } 247 248 + // SeedKnownDIDs inserts DIDs into known_dids, ignoring any that already exist. 249 + // Used for one-time migration from legacy storage. 250 + func (idx *FeedIndex) SeedKnownDIDs(dids []string) (int, error) { 251 + if len(dids) == 0 { 252 + return 0, nil 253 + } 254 + var added int 255 + for _, did := range dids { 256 + res, err := idx.db.Exec(`INSERT OR IGNORE INTO known_dids (did) VALUES (?)`, did) 257 + if err != nil { 258 + return added, fmt.Errorf("seed known DID %s: %w", did, err) 259 + } 260 + n, _ := res.RowsAffected() 261 + added += int(n) 262 + } 263 + return added, nil 264 + } 265 + 266 // Close closes the index database 267 func (idx *FeedIndex) Close() error { 268 if idx.db != nil {