---
title: Fix data_view socket cache TTL unit mismatch (ms vs seconds)
type: task
status: completed
description: >-
  The data_view websocket computes cache_ttl in milliseconds but passes it to redis as seconds, and
  cache_expire_at is null in the offseason -- so cached data-view results persist for ~250 days. Any
  query/data change needs manual cache invalidation/warm to surface.
base_uri: user:task/league/data-views/fix-data-view-socket-cache-ttl-unit.md
created_at: '2026-06-21T13:27:50.049Z'
entity_id: af17ec91-8e64-4f84-b05d-fd8cf6a2e46b
finished_at: '2026-06-21T13:50:04.915Z'
observations:
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-06-21T13:50:04.915Z'
visibility_analyzed_at: '2026-11-05T23:48:50.347Z'
---

# Fix data_view socket cache TTL unit mismatch

## Problem

In `api/sockets/data_view.mjs` (~line 159), the result cache TTL is computed in
milliseconds but handed to redis as a seconds value. Combined with
`cache_expire_at` being null during the offseason, cached data-view results
persist far longer than intended (on the order of ~250 days). The practical
consequence: any change to a data-view's computed values -- a query/SQL fix, a
data refresh, or a newly introduced column semantic -- will NOT surface to users
until the affected cache key is manually invalidated and re-warmed.

The cache key is `/data-views/${get_data_view_hash(params)}` (a hash of the full
request), so distinct views have distinct keys; the bug is purely the TTL
duration, not key collision.

## Why this is filed

Surfaced while implementing
[[user:task/league/data-views/make-team-rate-stat-attribution-configurable.md]].
That work's live verification depends on this: a user-visible value change (the
historical default OR a column flipped to `current`) will not appear without a
manual invalidate/warm. Filed as a discrete task so the relation graph is
self-describing; NOT fixed as part of that work.

## Fix direction (to be confirmed)

- Pass the TTL to redis in the unit it expects (seconds), or convert the
  ms value before the redis call.
- Decide offseason behavior for `cache_expire_at` (a sane bounded TTL rather
  than null).
- Audit any other call sites that compute a TTL in ms and pass it to the same
  redis helper.

## Verify

- A cached data-view result expires within the intended window (not ~250 days).
- Confirm against `api/sockets/data_view.mjs` cache set path.
