fix captain record check ttl

This commit is contained in:
Evan Jarrett
2026-05-02 11:11:01 -05:00
parent 13a793ca90
commit 35aa19e2b2
2 changed files with 36 additions and 8 deletions
+30 -8
View File
@@ -10,6 +10,7 @@ import (
"log/slog"
"net/http"
"strings"
"sync"
"time"
"atcr.io/pkg/appview/db"
@@ -18,6 +19,10 @@ import (
"atcr.io/pkg/auth/oauth"
)
// captainCheckTTL is how long a successful captain XRPC fetch suppresses
// further fetches for the same hold within this worker process.
const captainCheckTTL = 1 * time.Hour
// BackfillWorker uses com.atproto.sync.listReposByCollection to backfill historical data
type BackfillWorker struct {
db *sql.DB
@@ -26,6 +31,14 @@ type BackfillWorker struct {
defaultHoldDID string // Default hold DID from AppView config (e.g., "did:web:hold01.atcr.io")
testMode bool // If true, suppress warnings for external holds
refresher *oauth.Refresher // OAuth refresher for PDS writes (optional, can be nil)
// captainChecked tracks the last time we successfully fetched each hold's
// captain record from XRPC. This is the freshness gate for queryCaptainRecord
// — we can't rely on the DB row's updated_at because UpsertCaptainRecord
// intentionally skips writes (and thus the timestamp bump) when the captain
// data hasn't changed, to avoid round-tripping no-op writes to remote libsql.
captainCheckedMu sync.Mutex
captainChecked map[string]time.Time
}
// BackfillState tracks backfill progress
@@ -55,6 +68,7 @@ func NewBackfillWorker(database *sql.DB, relayEndpoints []string, defaultHoldDID
defaultHoldDID: defaultHoldDID,
testMode: testMode,
refresher: refresher,
captainChecked: make(map[string]time.Time),
}, nil
}
@@ -478,15 +492,19 @@ func (b *BackfillWorker) queryCaptainRecordWrapper(ctx context.Context, holdDID
return nil
}
// queryCaptainRecord queries a hold's captain record and caches it in the database
// queryCaptainRecord queries a hold's captain record and caches it in the database.
//
// Freshness is tracked in an in-memory map keyed by hold DID rather than the
// row's updated_at column. UpsertCaptainRecord deliberately skips no-op writes
// to avoid round-tripping unchanged data to remote libsql, which means
// updated_at is "last time data changed" not "last time we checked" — using it
// as a freshness gate would re-fetch on every call once the row aged past TTL.
func (b *BackfillWorker) queryCaptainRecord(ctx context.Context, holdDID string) error {
// Check if we already have it cached (skip if recently updated)
existing, err := db.GetCaptainRecord(b.db, holdDID)
if err == nil && existing != nil {
// If cached within last hour, skip refresh
if time.Since(existing.UpdatedAt) < 1*time.Hour {
return nil
}
b.captainCheckedMu.Lock()
last, ok := b.captainChecked[holdDID]
b.captainCheckedMu.Unlock()
if ok && time.Since(last) < captainCheckTTL {
return nil
}
// Resolve hold DID to URL
@@ -531,6 +549,10 @@ func (b *BackfillWorker) queryCaptainRecord(ctx context.Context, holdDID string)
return fmt.Errorf("failed to cache captain record: %w", err)
}
b.captainCheckedMu.Lock()
b.captainChecked[holdDID] = time.Now()
b.captainCheckedMu.Unlock()
slog.Info("Backfill cached captain record for hold", "hold_did", holdDID, "owner_did", captainRecord.OwnerDID)
return nil
}
+6
View File
@@ -223,6 +223,12 @@ func NewS3Service(params map[string]any) (*S3Service, error) {
// with trailers, which causes XAmzContentSHA256Mismatch errors on
// S3-compatible services that don't support this.
o.RequestChecksumCalculation = aws.RequestChecksumCalculationWhenRequired
// Mirror on the response side. Default is WhenSupported, which logs a
// WARN on every GetObject whose response lacks x-amz-checksum-* —
// i.e. every read against Storj/R2/MinIO/Backblaze. The bytes are fine,
// the warning is just SDK chatter; switch to WhenRequired so the SDK
// only validates when the server actually returned a checksum.
o.ResponseChecksumValidation = aws.ResponseChecksumValidationWhenRequired
})
var s3PathPrefix string