refactor(server): drop redis stream + worker role (#1792)
The Redis Stream `billing-events` + `worker` Railway role +
advisory-lock poller layered together didn't actually buy us reliability
— `debitFlux` swallowed XADD failures, leaving the door open to "balance
updated, ledger row never written". Collapse the whole thing back to:
`creditFlux` and `debitFlux` write `flux_transaction` ledger rows inline
within the same DB transaction that mutates `user_flux`, and `(user_id,
request_id)` remains the partial unique index that keeps retries safe.
Concrete changes:
- Inline ledger inserts in `BillingService.{debitFlux, creditFlux,
creditFluxFromStripeCheckout, creditFluxFromInvoice}`; drop `billingMq`
and `publishEvent` plumbing entirely.
- `routes/openai/v1` writes `llm_request_log` synchronously via the
existing `requestLogService`; the duplicate `llm-request-log.ts` service
module is removed.
- `bin/run-worker.ts`, `libs/mq/*`,
`services/billing/billing-events.ts`,
`services/billing/billing-consumer-handler.ts`, and matching tests are
deleted. CLI now exposes only `api`.
- `BILLING_EVENTS_*` env vars and the `DEFAULT_BILLING_EVENTS_STREAM`
helper are dropped; `docker-compose.yml` no longer ships a worker
service.
- `docs/ai-context/{workers-and-runtime, billing-architecture,
redis-boundaries-and-pubsub, data-model-and-state,
architecture-overview, README}.md`, `CLAUDE.md`, and the existing
verification docs are updated to describe the single-process synchronous
pipeline.
Tests: 29 files / 247 cases pass. Production deployments need to drop
the worker Railway service after this lands.
This commit is contained in:
+21
-28
@@ -3,10 +3,8 @@ import type Redis from 'ioredis'
|
||||
import type { AuthInstance } from './libs/auth'
|
||||
import type { Database } from './libs/db'
|
||||
import type { Env } from './libs/env'
|
||||
import type { MqService } from './libs/mq'
|
||||
import type { OtelInstance } from './libs/otel'
|
||||
import type { FluxGrantBatchService } from './services/admin-flux-grant-batch/flux-grant-batch-service'
|
||||
import type { BillingEvent } from './services/billing/billing-events'
|
||||
import type { AdminFluxGrantsService } from './services/admin-flux-grants'
|
||||
import type { BillingService } from './services/billing/billing-service'
|
||||
import type { FluxMeter } from './services/billing/flux-meter'
|
||||
import type { CharacterService } from './services/characters'
|
||||
@@ -15,6 +13,7 @@ import type { ConfigKVService } from './services/config-kv'
|
||||
import type { FluxService } from './services/flux'
|
||||
import type { FluxTransactionService } from './services/flux-transaction'
|
||||
import type { ProviderService } from './services/providers'
|
||||
import type { RequestLogService } from './services/request-log'
|
||||
import type { StripeService } from './services/stripe'
|
||||
import type { UserDeletionService } from './services/user-deletion'
|
||||
import type { HonoEnv } from './types/hono'
|
||||
@@ -41,7 +40,7 @@ import { createRedis } from './libs/redis'
|
||||
import { resolveRequestAuth } from './libs/request-auth'
|
||||
import { sessionMiddleware } from './middlewares/auth'
|
||||
import { otelMiddleware } from './middlewares/otel'
|
||||
import { createAdminFluxGrantBatchRoutes } from './routes/admin/flux-grant-batches'
|
||||
import { createAdminFluxGrantsRoutes } from './routes/admin/flux-grants'
|
||||
import { createAuthRoutes } from './routes/auth'
|
||||
import { createCharacterRoutes } from './routes/characters'
|
||||
import { createChatWsHandlers } from './routes/chat-ws'
|
||||
@@ -50,8 +49,7 @@ import { createFluxRoutes } from './routes/flux'
|
||||
import { createV1CompletionsRoutes } from './routes/openai/v1'
|
||||
import { createProviderRoutes } from './routes/providers'
|
||||
import { createStripeRoutes } from './routes/stripe'
|
||||
import { createFluxGrantBatchService } from './services/admin-flux-grant-batch/flux-grant-batch-service'
|
||||
import { createBillingMq } from './services/billing/billing-events'
|
||||
import { createAdminFluxGrantsService } from './services/admin-flux-grants'
|
||||
import { createBillingService } from './services/billing/billing-service'
|
||||
import { createFluxMeter } from './services/billing/flux-meter'
|
||||
import { createCharacterService } from './services/characters'
|
||||
@@ -78,9 +76,9 @@ interface AppDeps {
|
||||
fluxTransactionService: FluxTransactionService
|
||||
stripeService: StripeService
|
||||
billingService: BillingService
|
||||
fluxGrantBatchService: FluxGrantBatchService
|
||||
adminFluxGrantsService: AdminFluxGrantsService
|
||||
ttsMeter: FluxMeter
|
||||
billingMq: MqService<BillingEvent>
|
||||
requestLogService: RequestLogService
|
||||
configKV: ConfigKVService
|
||||
redis: Redis
|
||||
env: Env
|
||||
@@ -213,7 +211,7 @@ export async function buildApp(deps: AppDeps) {
|
||||
/**
|
||||
* V1 routes for official provider.
|
||||
*/
|
||||
.route('/api/v1/openai', createV1CompletionsRoutes(deps.fluxService, deps.billingService, deps.configKV, deps.billingMq, deps.ttsMeter, deps.redis, deps.env, deps.otel?.genAi))
|
||||
.route('/api/v1/openai', createV1CompletionsRoutes(deps.fluxService, deps.billingService, deps.configKV, deps.requestLogService, deps.ttsMeter, deps.redis, deps.env, deps.otel?.genAi))
|
||||
|
||||
/**
|
||||
* Flux routes.
|
||||
@@ -226,10 +224,10 @@ export async function buildApp(deps: AppDeps) {
|
||||
.route('/api/v1/stripe', createStripeRoutes(deps.fluxService, deps.stripeService, deps.billingService, deps.configKV, deps.env, deps.redis, deps.otel?.revenue))
|
||||
|
||||
/**
|
||||
* Admin routes — guarded by ADMIN_USER_IDS allowlist. v1 only includes
|
||||
* batch-based promo flux grant operations.
|
||||
* Admin routes — guarded by `ADMIN_EMAILS` allowlist + verified email.
|
||||
* v1 only includes synchronous one-shot promo flux grants.
|
||||
*/
|
||||
.route('/api/admin/flux-grant-batches', createAdminFluxGrantBatchRoutes(deps.fluxGrantBatchService, deps.env))
|
||||
.route('/api/admin/flux-grants', createAdminFluxGrantsRoutes(deps.adminFluxGrantsService, deps.env))
|
||||
|
||||
/**
|
||||
* Catch-all 404 in JSON. Replaces hono's default `text/html` "404 Not
|
||||
@@ -330,13 +328,6 @@ export async function createApp() {
|
||||
build: ({ dependsOn }) => createConfigKVService(dependsOn.redis),
|
||||
})
|
||||
|
||||
const billingMq = injeca.provide('services:billingMq', {
|
||||
dependsOn: { redis, env: parsedEnv },
|
||||
build: ({ dependsOn }) => createBillingMq(dependsOn.redis, {
|
||||
stream: dependsOn.env.BILLING_EVENTS_STREAM,
|
||||
}),
|
||||
})
|
||||
|
||||
const emailService = injeca.provide('services:email', {
|
||||
dependsOn: { env: parsedEnv },
|
||||
build: ({ dependsOn }) => createEmailService({
|
||||
@@ -429,13 +420,16 @@ export async function createApp() {
|
||||
})
|
||||
|
||||
const billingService = injeca.provide('services:billing', {
|
||||
dependsOn: { db, redis, billingMq, configKV, otel },
|
||||
build: ({ dependsOn }) => createBillingService(dependsOn.db, dependsOn.redis, dependsOn.billingMq, dependsOn.configKV, dependsOn.otel?.revenue),
|
||||
dependsOn: { db, redis, configKV, otel },
|
||||
build: ({ dependsOn }) => createBillingService(dependsOn.db, dependsOn.redis, dependsOn.configKV, dependsOn.otel?.revenue),
|
||||
})
|
||||
|
||||
const fluxGrantBatchService = injeca.provide('services:adminFluxGrantBatch', {
|
||||
dependsOn: { db },
|
||||
build: ({ dependsOn }) => createFluxGrantBatchService(dependsOn.db),
|
||||
const adminFluxGrantsService = injeca.provide('services:adminFluxGrants', {
|
||||
dependsOn: { db, billingService },
|
||||
build: ({ dependsOn }) => createAdminFluxGrantsService({
|
||||
db: dependsOn.db,
|
||||
billingService: dependsOn.billingService,
|
||||
}),
|
||||
})
|
||||
|
||||
const ttsMeter = injeca.provide('services:ttsMeter', {
|
||||
@@ -468,9 +462,8 @@ export async function createApp() {
|
||||
requestLogService,
|
||||
stripeService,
|
||||
billingService,
|
||||
fluxGrantBatchService,
|
||||
adminFluxGrantsService,
|
||||
ttsMeter,
|
||||
billingMq,
|
||||
configKV,
|
||||
redis,
|
||||
env: parsedEnv,
|
||||
@@ -487,9 +480,9 @@ export async function createApp() {
|
||||
fluxTransactionService: resolved.fluxTransactionService,
|
||||
stripeService: resolved.stripeService,
|
||||
billingService: resolved.billingService,
|
||||
fluxGrantBatchService: resolved.fluxGrantBatchService,
|
||||
adminFluxGrantsService: resolved.adminFluxGrantsService,
|
||||
ttsMeter: resolved.ttsMeter,
|
||||
billingMq: resolved.billingMq,
|
||||
requestLogService: resolved.requestLogService,
|
||||
configKV: resolved.configKV,
|
||||
redis: resolved.redis,
|
||||
env: resolved.env,
|
||||
|
||||
Reference in New Issue
Block a user