Files
anomalyco_opencode/packages/stats/server/src/stat-sync.ts
T
opencode-agent[bot] 0dcedc199c chore: merge dev into v2 (#41460)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-10 03:36:24 +00:00

61 lines
2.4 KiB
TypeScript

import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import { Athena } from "@opencode-ai/stats-core/athena"
import { ModelStatRepo } from "@opencode-ai/stats-core/domain/model"
import { layer as statsLayer } from "@opencode-ai/stats-core/runtime"
import { syncStats } from "@opencode-ai/stats-core/stat-sync"
import { Cause, Duration, Effect, Layer, Schedule } from "effect"
const SYNC_INTERVAL = "1 hour"
const SYNC_INTERVAL_MS = 3_600_000
const runtimeLayer = Layer.mergeAll(statsLayer, Athena.layer)
const daemon = Effect.gen(function* () {
yield* Effect.logInfo("stats sync daemon started")
yield* initialDelay()
// One full pass per UTC day (including the first pass after boot) refreshes the
// whole display window; every other pass only recomputes the current ISO week.
let lastFullDay = ""
const pass = Effect.gen(function* () {
const today = new Date().toISOString().slice(0, 10)
if (lastFullDay !== today) {
const completed = yield* syncStats({ full: true }).pipe(
Effect.as(true),
Effect.catchCause((cause) =>
Effect.logWarning(`full stats sync failed; falling back to incremental sync ${Cause.pretty(cause)}`).pipe(
Effect.as(false),
),
),
)
lastFullDay = today
if (completed) return
}
yield* syncStats({ full: false })
}).pipe(
Effect.catchCause((cause) =>
Effect.logWarning(`stats sync failed ${JSON.stringify({ cause: Cause.pretty(cause) })}`),
),
)
yield* pass.pipe(Effect.repeat(Schedule.fixed(SYNC_INTERVAL)))
}).pipe(Effect.forkScoped)
// A restarted daemon must not immediately re-run the expensive Athena pass; resume
// the hourly cadence from the last completed sync instead. This caps the Athena
// spend of a crash loop at one pass per interval.
const initialDelay = Effect.fnUntraced(function* () {
const modelStats = yield* ModelStatRepo
const lastSynced = yield* modelStats.lastSyncedAt().pipe(Effect.catchCause(() => Effect.succeed(null)))
if (!lastSynced) return
const delayMs = Math.min(SYNC_INTERVAL_MS - (Date.now() - lastSynced.getTime()), SYNC_INTERVAL_MS)
if (delayMs <= 0) return
yield* Effect.logInfo(
`stats sync delaying first pass ${JSON.stringify({ lastSyncedAt: lastSynced.toISOString(), delayMs })}`,
)
yield* Effect.sleep(Duration.millis(delayMs))
})
NodeRuntime.runMain(Layer.launch(Layer.effectDiscard(daemon).pipe(Layer.provide(runtimeLayer))), {
disableErrorReporting: true,
})