Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
3.9 KiB
Go
128 lines
3.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"evobgp/internal/store"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
func (p *Postgres) GetRenderGeneration(ctx context.Context, tenantID string) (int64, error) {
|
|
var generation int64
|
|
err := p.pool.QueryRow(ctx, `SELECT generation FROM tenant_render_generation WHERE tenant_id=$1::uuid`, tenantID).Scan(&generation)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return 0, store.ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return 0, fmt.Errorf("repository: render generation: %w", err)
|
|
}
|
|
return generation, nil
|
|
}
|
|
|
|
func (p *Postgres) ListModuleSnapshotHeaders(ctx context.Context, tenantID string) ([]store.ModuleSnapshotUse, error) {
|
|
rows, err := p.pool.Query(ctx, `
|
|
SELECT module_id::text, snapshot_version, COALESCE(input_hash, '')
|
|
FROM module_prefix_snapshot
|
|
WHERE tenant_id = $1::uuid
|
|
ORDER BY module_id`, tenantID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("repository: list snapshot headers: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
var out []store.ModuleSnapshotUse
|
|
for rows.Next() {
|
|
var item store.ModuleSnapshotUse
|
|
if err := rows.Scan(&item.ModuleID, &item.SnapshotVersion, &item.InputHash); err != nil {
|
|
return nil, fmt.Errorf("repository: scan snapshot header: %w", err)
|
|
}
|
|
out = append(out, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("repository: iterate snapshot headers: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Postgres) LatestRenderManifest(ctx context.Context, tenantID string) (*store.RenderManifestRecord, bool, error) {
|
|
var revID string
|
|
var gen *int64
|
|
var raw []byte
|
|
err := p.pool.QueryRow(ctx, `
|
|
SELECT cr.id::text, m.config_generation, m.manifest_json
|
|
FROM config_revision cr
|
|
LEFT JOIN render_revision_manifest m ON m.revision_id = cr.id
|
|
WHERE cr.tenant_id = $1::uuid
|
|
ORDER BY cr.created_at DESC, cr.id DESC
|
|
LIMIT 1`, tenantID).Scan(&revID, &gen, &raw)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, false, nil
|
|
}
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("repository: latest render manifest: %w", err)
|
|
}
|
|
if gen == nil || len(raw) == 0 {
|
|
return nil, false, nil
|
|
}
|
|
var doc struct {
|
|
Snapshots []store.ModuleSnapshotUse `json:"snapshots"`
|
|
}
|
|
if err := json.Unmarshal(raw, &doc); err != nil {
|
|
return nil, false, fmt.Errorf("repository: decode render manifest: %w", err)
|
|
}
|
|
return &store.RenderManifestRecord{
|
|
RevisionID: revID,
|
|
Generation: *gen,
|
|
Snapshots: doc.Snapshots,
|
|
}, true, nil
|
|
}
|
|
|
|
func insertRenderManifestTx(ctx context.Context, tx pgx.Tx, revisionID, tenantID string) error {
|
|
var gen int64
|
|
err := tx.QueryRow(ctx, `
|
|
SELECT generation FROM tenant_render_generation
|
|
WHERE tenant_id = $1::uuid
|
|
FOR UPDATE`, tenantID).Scan(&gen)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
gen = 1
|
|
} else if err != nil {
|
|
return fmt.Errorf("repository: lock render generation: %w", err)
|
|
}
|
|
rows, err := tx.Query(ctx, `
|
|
SELECT module_id::text, snapshot_version, COALESCE(input_hash, '')
|
|
FROM module_prefix_snapshot
|
|
WHERE tenant_id = $1::uuid
|
|
ORDER BY module_id`, tenantID)
|
|
if err != nil {
|
|
return fmt.Errorf("repository: read snapshot versions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
var snaps []store.ModuleSnapshotUse
|
|
for rows.Next() {
|
|
var item store.ModuleSnapshotUse
|
|
if err := rows.Scan(&item.ModuleID, &item.SnapshotVersion, &item.InputHash); err != nil {
|
|
return fmt.Errorf("repository: scan snapshot version: %w", err)
|
|
}
|
|
snaps = append(snaps, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return fmt.Errorf("repository: iterate snapshot versions: %w", err)
|
|
}
|
|
body, err := json.Marshal(struct {
|
|
Snapshots []store.ModuleSnapshotUse `json:"snapshots"`
|
|
}{Snapshots: snaps})
|
|
if err != nil {
|
|
return fmt.Errorf("repository: encode render manifest: %w", err)
|
|
}
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO render_revision_manifest (revision_id, tenant_id, config_generation, manifest_json)
|
|
VALUES ($1::uuid, $2::uuid, $3, $4::jsonb)`,
|
|
revisionID, tenantID, gen, body); err != nil {
|
|
return fmt.Errorf("repository: insert render manifest: %w", err)
|
|
}
|
|
return nil
|
|
}
|