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.
This commit is contained in:
zenfun
2026-01-07 13:42:25 +08:00
parent c19c26817d
commit e35e801839
2 changed files with 43 additions and 8 deletions

View File

@@ -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
}

View File

@@ -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) {