feat(sol): align canary ingestor and api auth
All checks were successful
deploy-trade-r001-canary / apply (push) Successful in 6m14s

This commit is contained in:
mpabi
2026-04-12 18:30:30 +02:00
parent 59507521d6
commit 2e909026a7
5 changed files with 199 additions and 9 deletions

View File

@@ -50,9 +50,10 @@ Minimal canary namespace for migration baseline `R001` on `sol`.
- The canary workflow re-runs:
- `postgres-migrate`
- `hasura-bootstrap`
- `api-token-seed`
before it waits for `Hasura`, `trade-api`, `trade-frontend`, `trade-ingestor`, and the DLOB hot/all-path deployments to become healthy.
- The current canary `trade-ingestor` is intentionally pinned to the schema already reconstructed on `sol` and reads from `dlob_stats_latest`.
- The exact live `R001` ingestor path that reads `dlob_*_derived_latest` remains a follow-up substep after the DLOB writer chain is reconstructed.
- The canary `trade-ingestor` now follows the live `R001` path on `sol`: it reads `dlob_hot_derived_latest` first for hot markets and falls back to `dlob_all_derived_latest`.
- `api-token-seed` restores the frontend read token in `api_tokens`, so `trade-api` and `trade-frontend` can be validated against the reconstructed derived tables after each deploy.
## Operator Flow

View File

@@ -0,0 +1,76 @@
apiVersion: batch/v1
kind: Job
metadata:
name: api-token-seed
namespace: trade-r001-canary
spec:
backoffLimit: 1
template:
spec:
restartPolicy: OnFailure
containers:
- name: seed
image: postgres:16-alpine
imagePullPolicy: IfNotPresent
command:
- sh
- -lc
- |
set -euo pipefail
read_json="$(tr -d '\n' </tokens/read.json)"
token="$(printf '%s' "$read_json" | sed -E 's/.*"token"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
name="$(printf '%s' "$read_json" | sed -E 's/.*"name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
test -n "$token"
test -n "$name"
token_hash="$(printf '%s' "$token" | sha256sum | awk '{print $1}')"
export PGPASSWORD="$POSTGRES_PASSWORD"
psql "host=$PGHOST port=$PGPORT dbname=$POSTGRES_DB user=$POSTGRES_USER" \
-v token_name="$name" \
-v token_hash="$token_hash" \
-v token_meta='{"seed":"trade-frontend-tokens/read.json","namespace":"trade-r001-canary","scopes":["read"]}' <<'SQL'
INSERT INTO api_tokens (name, token_hash, scopes, meta, revoked_at)
VALUES (
:'token_name',
:'token_hash',
ARRAY['read'],
(:'token_meta')::jsonb,
NULL
)
ON CONFLICT (token_hash) DO UPDATE
SET name = EXCLUDED.name,
scopes = EXCLUDED.scopes,
meta = EXCLUDED.meta,
revoked_at = NULL;
SQL
env:
- name: PGHOST
value: postgres
- name: PGPORT
value: "5432"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: trade-postgres
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: trade-postgres
key: POSTGRES_PASSWORD
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: trade-postgres
key: POSTGRES_DB
volumeMounts:
- name: frontend-tokens
mountPath: /tokens
readOnly: true
volumes:
- name: frontend-tokens
secret:
secretName: trade-frontend-tokens

View File

@@ -101,10 +101,22 @@ async function graphqlRequest(cfg, query, variables) {
async function fetchStats(cfg) {
const query = `
query DlobStatsLatest($markets: [String!]!) {
dlob_stats_latest(where: { market_name: { _in: $markets } }) {
dlob_hot_derived_latest(where: { market_name: { _in: $markets }, is_indicative: { _eq: false } }) {
market_name
market_index
ts
ts_ms
slot
oracle_price
mark_price
mid_price
best_bid_price
best_ask_price
updated_at
}
dlob_all_derived_latest(where: { market_name: { _in: $markets }, is_indicative: { _eq: false } }) {
market_name
market_index
ts_ms
slot
oracle_price
mark_price
@@ -117,7 +129,21 @@ async function fetchStats(cfg) {
`;
const data = await graphqlRequest(cfg, query, { markets: cfg.markets });
return Array.isArray(data?.dlob_stats_latest) ? data.dlob_stats_latest : [];
const merged = new Map();
for (const row of Array.isArray(data?.dlob_all_derived_latest) ? data.dlob_all_derived_latest : []) {
const marketName = String(row?.market_name || '').trim();
if (!marketName) continue;
merged.set(marketName, { ...row, __from: 'dlob_all_derived_latest' });
}
for (const row of Array.isArray(data?.dlob_hot_derived_latest) ? data.dlob_hot_derived_latest : []) {
const marketName = String(row?.market_name || '').trim();
if (!marketName) continue;
merged.set(marketName, { ...row, __from: 'dlob_hot_derived_latest' });
}
return Array.from(merged.values());
}
async function insertTicks(cfg, objects) {
@@ -168,7 +194,7 @@ async function main() {
if (updatedAt) lastUpdatedAtByMarket.set(marketName, updatedAt);
const marketIndex = toIntOrNull(r?.market_index) ?? 0;
const dlobIso = isoFromEpochMs(r?.ts);
const dlobIso = isoFromEpochMs(r?.ts_ms);
const tsIso = dlobIso || nowIso;
const oraclePrice = numStr(r?.oracle_price) || numStr(r?.mark_price) || numStr(r?.mid_price);
@@ -184,11 +210,11 @@ async function main() {
oracle_slot: r?.slot == null ? null : String(r.slot),
source: cfg.source,
raw: {
from: 'dlob_stats_latest',
from: r?.__from || 'dlob_all_derived_latest',
market_name: marketName,
market_index: marketIndex,
dlob: {
ts: r?.ts ?? null,
ts_ms: r?.ts_ms ?? null,
slot: r?.slot ?? null,
best_bid_price: r?.best_bid_price ?? null,
best_ask_price: r?.best_ask_price ?? null,

View File

@@ -16,6 +16,7 @@ resources:
- hasura-deployment.yaml
- postgres-migrate-job.yaml
- hasura-bootstrap-job.yaml
- api-token-seed-job.yaml
- trade-api-service.yaml
- trade-api-deployment.yaml
- trade-frontend-service.yaml