···143143 slog.Info("Stats migration completed", "component", "migration",
144144 "success", successCount, "skipped", skipCount, "errors", errorCount, "total", len(stats))
145145146146- // Mark migration complete (even if some failed - they'll get updates via Jetstream)
147147- return markMigrationComplete(db)
146146+ // Only mark complete if there were no errors
147147+ // Skipped repos (no hold DID) will never migrate - that's fine
148148+ // Errors are transient failures that should be retried
149149+ if errorCount == 0 {
150150+ return markMigrationComplete(db)
151151+ }
152152+153153+ slog.Warn("Stats migration had errors, will retry on next startup", "component", "migration",
154154+ "errors", errorCount)
155155+ return nil
148156}
149157150158// markMigrationComplete records that the stats migration has been done
+4-1
pkg/hold/oci/xrpc.go
···40404141// RegisterHandlers registers all OCI XRPC endpoints with the chi router
4242func (h *XRPCHandler) RegisterHandlers(r chi.Router) {
4343+ // Temporary migration endpoint - no auth required
4444+ // TODO: Remove after stats migration is complete
4545+ r.Post(atproto.HoldSetStats, h.HandleSetStats)
4646+4347 // All multipart upload endpoints require blob:write permission
4448 r.Group(func(r chi.Router) {
4549 r.Use(h.requireBlobWriteAccess)
···5054 r.Post(atproto.HoldCompleteUpload, h.HandleCompleteUpload)
5155 r.Post(atproto.HoldAbortUpload, h.HandleAbortUpload)
5256 r.Post(atproto.HoldNotifyManifest, h.HandleNotifyManifest)
5353- r.Post(atproto.HoldSetStats, h.HandleSetStats)
5457 })
5558}
5659