From e35e801839f27f9ceb845631ba516de7fbb546b8 Mon Sep 17 00:00:00 2001 From: zenfun Date: Wed, 7 Jan 2026 13:42:25 +0800 Subject: [PATCH] feat(service): add provider metadata tracking Write 'meta:providers_meta' to Redis during provider synchronization, including version, timestamp, and configuration checksum. This aligns provider sync with model metadata handling and enables better cache invalidation. --- internal/service/sync.go | 41 ++++++++++++++++++++++++++++------- internal/service/sync_test.go | 10 +++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/internal/service/sync.go b/internal/service/sync.go index b56374b..b554875 100644 --- a/internal/service/sync.go +++ b/internal/service/sync.go @@ -176,8 +176,12 @@ func (s *SyncService) SyncProvidersNow(ctx context.Context, db *gorm.DB) error { } pipe := s.rdb.TxPipeline() - pipe.Del(ctx, "config:providers") - if err := s.writeProvidersSnapshot(ctx, pipe, groups, apiKeys); err != nil { + pipe.Del(ctx, "config:providers", "meta:providers_meta") + payloads, err := s.writeProvidersSnapshot(ctx, pipe, groups, apiKeys) + if err != nil { + return err + } + if err := writeProvidersMeta(ctx, pipe, payloads); err != nil { return err } if _, err := pipe.Exec(ctx); err != nil { @@ -296,12 +300,13 @@ type providerSnapshot struct { BanUntil int64 `json:"ban_until,omitempty"` // unix seconds } -func (s *SyncService) writeProvidersSnapshot(ctx context.Context, pipe redis.Pipeliner, groups []model.ProviderGroup, apiKeys []model.APIKey) error { +func (s *SyncService) writeProvidersSnapshot(ctx context.Context, pipe redis.Pipeliner, groups []model.ProviderGroup, apiKeys []model.APIKey) (map[string]string, error) { groupMap := make(map[uint]model.ProviderGroup, len(groups)) for _, g := range groups { groupMap[g.ID] = g } + payloads := make(map[string]string, len(apiKeys)) for _, k := range apiKeys { g, ok := groupMap[k.GroupID] if !ok { @@ -363,11 +368,13 @@ func (s *SyncService) writeProvidersSnapshot(ctx context.Context, pipe redis.Pip payload, err := jsoncodec.Marshal(snap) if err != nil { - return fmt.Errorf("marshal provider %d: %w", k.ID, err) + return nil, fmt.Errorf("marshal provider %d: %w", k.ID, err) } - pipe.HSet(ctx, "config:providers", fmt.Sprintf("%d", k.ID), payload) + key := fmt.Sprintf("%d", k.ID) + pipe.HSet(ctx, "config:providers", key, payload) + payloads[key] = string(payload) } - return nil + return payloads, nil } // keySnapshot is no longer needed as we write directly to auth:token:* @@ -423,7 +430,7 @@ func (s *SyncService) SyncAllNow(ctx context.Context, db *gorm.DB) error { } pipe := s.rdb.TxPipeline() - pipe.Del(ctx, "config:providers", "config:keys", "meta:models", "meta:models_meta", "config:bindings", "meta:bindings_meta") + pipe.Del(ctx, "config:providers", "meta:providers_meta", "config:keys", "meta:models", "meta:models_meta", "config:bindings", "meta:bindings_meta") // Also clear master keys var masterKeys []string iter := s.rdb.Scan(ctx, 0, "auth:master:*", 0).Iterator() @@ -437,7 +444,11 @@ func (s *SyncService) SyncAllNow(ctx context.Context, db *gorm.DB) error { pipe.Del(ctx, masterKeys...) } - if err := s.writeProvidersSnapshot(ctx, pipe, groups, apiKeys); err != nil { + providersPayloads, err := s.writeProvidersSnapshot(ctx, pipe, groups, apiKeys) + if err != nil { + return err + } + if err := writeProvidersMeta(ctx, pipe, providersPayloads); err != nil { return err } @@ -839,3 +850,17 @@ func writeModelsMeta(ctx context.Context, pipe redis.Pipeliner, meta modelcap.Me } return nil } + +func writeProvidersMeta(ctx context.Context, pipe redis.Pipeliner, payloads map[string]string) error { + now := time.Now() + fields := map[string]string{ + "version": fmt.Sprintf("%d", now.UnixNano()), + "updated_at": fmt.Sprintf("%d", now.Unix()), + "source": "db", + "checksum": modelcap.ChecksumFromPayloads(payloads), + } + if err := pipe.HSet(ctx, "meta:providers_meta", fields).Err(); err != nil { + return fmt.Errorf("write meta:providers_meta: %w", err) + } + return nil +} diff --git a/internal/service/sync_test.go b/internal/service/sync_test.go index f5b9b9b..1193436 100644 --- a/internal/service/sync_test.go +++ b/internal/service/sync_test.go @@ -61,6 +61,16 @@ func TestSyncProviders_WritesSnapshot(t *testing.T) { if snap["group"] != "default" { t.Fatalf("expected group default, got %#v", snap["group"]) } + + if v := mr.HGet("meta:providers_meta", "version"); v == "" { + t.Fatalf("expected meta:providers_meta.version") + } + if v := mr.HGet("meta:providers_meta", "updated_at"); v == "" { + t.Fatalf("expected meta:providers_meta.updated_at") + } + if v := mr.HGet("meta:providers_meta", "checksum"); v == "" { + t.Fatalf("expected meta:providers_meta.checksum") + } } func TestSyncKey_WritesTokenID(t *testing.T) {