Operations

Configure, monitor, and troubleshoot PolicyOne's ClaimsEventsConsumer in production.

← Large Loss Workflow


Environment Variables

VariableDefaultDescription
REDIS_HOSTlocalhostRedis hostname
REDIS_PORT6379Redis port
REDIS_PASSWORD(unset)Redis AUTH password
DISABLE_WORKERSfalseSet to "true" to skip consumer startup (e.g. in CI)
DATABASE_URL(required)PolicyOne PostgreSQL connection string

PolicyOne does not set OUTBOX_POLL_MS or STREAM_MAXLEN. Those are ClaimsOne's publisher settings. PolicyOne only needs Redis connectivity to consume.

.env.local Example

# Redis (must match ClaimsOne's Redis instance)
REDIS_HOST=redis.internal.acme.com
REDIS_PORT=6379
REDIS_PASSWORD=s3cr3t

DISABLE_WORKERS=false

Consumer Startup

ClaimsEventsConsumer starts automatically via PolicyOne's instrumentation.ts:

// apps/policyone/instrumentation.ts (relevant section)
export async function register() {
  if (
    process.env.NEXT_RUNTIME === 'nodejs' &&
    process.env.DISABLE_WORKERS !== 'true'
  ) {
    const { OutboxPublisher } = await import('./lib/workers/outbox-publisher.ts')
    const { ClaimsEventsConsumer } = await import('./lib/workers/claims-events-consumer.ts')

    const publisher = new OutboxPublisher()
    const claimsConsumer = new ClaimsEventsConsumer()

    await publisher.start()
    await claimsConsumer.start()

    console.log('[instrumentation] PolicyOne workers started: OutboxPublisher, ClaimsEventsConsumer')

    const shutdown = () => {
      void publisher?.stop()
      claimsConsumer.stop()
    }
    process.once('SIGTERM', shutdown)
    process.once('SIGINT', shutdown)
  }
}

The consumer creates the policy-center consumer group on startup if it doesn't exist (XGROUP CREATE ... $ MKSTREAM).


Redis Monitoring

# Check consumer group status for policy-center
redis-cli XINFO GROUPS events:claims

# Expected output (healthy):
# name: policy-center
#   consumers: 1
#   pending: 0        ← should be 0 in steady state
#   last-delivered-id: 1718000000000-0
#   lag: 0            ← should be 0 (caught up)

# Show pending (unacknowledged) messages for policy-center
redis-cli XPENDING events:claims policy-center - + 10

# How far behind is policy-center?
# (compare last-delivered-id to XREVRANGE + -)
redis-cli XREVRANGE events:claims + - COUNT 1

Database Monitoring

-- Count agent tasks by status (large_loss_review only)
SELECT status, COUNT(*) AS count
FROM policy.agent_tasks
WHERE task_kind = 'large_loss_review'
  AND tenant_id = $1
GROUP BY status
ORDER BY count DESC;

-- Tasks pending > 7 days (SLA breach risk)
SELECT
  at.id,
  at.human_review_note,
  at.created_at,
  now() - at.created_at AS age,
  p.policy_number
FROM policy.agent_tasks at
JOIN policy.policies p ON p.id = at.entity_id
WHERE at.task_kind = 'large_loss_review'
  AND at.status = 'pending'
  AND at.created_at < now() - interval '7 days'
  AND at.tenant_id = $1;

Troubleshooting

Troubleshooting Checklist

Rendering diagram…

Consumer not starting:

  • [instrumentation] PolicyOne workers started log line present
  • DISABLE_WORKERS !== 'true' in .env.local
  • NEXT_RUNTIME === 'nodejs' — not Edge mode

Agent tasks not being created:

  • The claim.settled event has isLargeLoss: true (check XRANGE events:claims - + COUNT 5)
  • policyNumber in the event matches a row in policy.policies for the correct tenant_id
  • PolicyOne's database is reachable (SELECT 1 from the app host)

Consumer lag > 0 for extended time:

  • Check PolicyOne process memory and CPU
  • Confirm PostgreSQL connection pool is not exhausted
  • Check for lock contention on policy.agent_tasks

← Previous: Large Loss Workflow