Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ PRISM_PUBLIC_CACHE_TTL_SECONDS=5
PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS=30
PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS=300
PRISM_PUBLIC_CONFIG_CACHE_STALE_WHILE_REVALIDATE_SECONDS=3600
PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS=
PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS=
PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS=86400
PRISM_PUBLIC_ARTIFACT_CACHE_STALE_WHILE_REVALIDATE_SECONDS=86400
PRISM_PUBLIC_CACHE_MAX_ENTRIES=512
Expand Down
2 changes: 2 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ services:
PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS: ${PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS:-30}
PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS: ${PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS:-300}
PRISM_PUBLIC_CONFIG_CACHE_STALE_WHILE_REVALIDATE_SECONDS: ${PRISM_PUBLIC_CONFIG_CACHE_STALE_WHILE_REVALIDATE_SECONDS:-3600}
PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS: ${PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS:-}
PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS: ${PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS:-}
PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS: ${PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS:-86400}
PRISM_PUBLIC_ARTIFACT_CACHE_STALE_WHILE_REVALIDATE_SECONDS: ${PRISM_PUBLIC_ARTIFACT_CACHE_STALE_WHILE_REVALIDATE_SECONDS:-86400}
PRISM_PUBLIC_CACHE_MAX_ENTRIES: ${PRISM_PUBLIC_CACHE_MAX_ENTRIES:-512}
Expand Down
12 changes: 10 additions & 2 deletions docs/public-dashboard-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ emits conservative browser caching (`Cache-Control: public, max-age=0,
must-revalidate`) plus shared-cache headers for CDNs such as Vercel. Dynamic
dashboard read models default to a 5-second shared-cache TTL with 30 seconds of
`stale-while-revalidate`. `GET /public/v1/mining-configuration` defaults to 300
seconds, and content-addressed artifact routes default to 86400 seconds with an
immutable shared-cache hint.
seconds. Settlement-artifact responses also default to 5 seconds because they
include live fanout status and broadcast attempts. Content-addressed artifact
routes default to 86400 seconds with an immutable shared-cache hint.

Settlement-specific cache controls are blank by default and inherit
`PRISM_PUBLIC_CACHE_TTL_SECONDS` and
`PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS`. Set them only when an
explicit route-specific override is required.

Operators can tune the defaults with:

Expand All @@ -49,6 +55,8 @@ Operators can tune the defaults with:
- `PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS`
- `PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS`
- `PRISM_PUBLIC_CONFIG_CACHE_STALE_WHILE_REVALIDATE_SECONDS`
- `PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS`
- `PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS`
- `PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS`
- `PRISM_PUBLIC_ARTIFACT_CACHE_STALE_WHILE_REVALIDATE_SECONDS`
- `PRISM_PUBLIC_CACHE_MAX_ENTRIES`
Expand Down
25 changes: 20 additions & 5 deletions lab/prism/public_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ def public_cache_key(path: str, query: dict[str, list[str]]) -> tuple[str, tuple
def public_cache_policy(path: str) -> PublicCachePolicy:
if not env_bool("PRISM_PUBLIC_CACHE_ENABLED", default=True):
return PublicCachePolicy(ttl_seconds=0, stale_while_revalidate_seconds=0)
dynamic_ttl_seconds = env_nonnegative_int("PRISM_PUBLIC_CACHE_TTL_SECONDS", 5)
dynamic_stale_while_revalidate_seconds = env_nonnegative_int(
"PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS",
30,
)
if path == "/public/v1/mining-configuration":
return PublicCachePolicy(
ttl_seconds=env_nonnegative_int("PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS", 300),
Expand All @@ -196,6 +201,19 @@ def public_cache_policy(path: str) -> PublicCachePolicy:
3600,
),
)
if path.startswith("/public/v1/blocks/") and path.endswith("/settlement-artifacts"):
block_hash = path[len("/public/v1/blocks/"):-len("/settlement-artifacts")].strip()
if _is_hex64(block_hash):
return PublicCachePolicy(
ttl_seconds=env_nonnegative_int(
"PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS",
dynamic_ttl_seconds,
),
stale_while_revalidate_seconds=env_nonnegative_int(
"PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS",
dynamic_stale_while_revalidate_seconds,
),
)
if path.startswith("/public/v1/artifacts/"):
return PublicCachePolicy(
ttl_seconds=env_nonnegative_int("PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS", 86400),
Expand All @@ -206,11 +224,8 @@ def public_cache_policy(path: str) -> PublicCachePolicy:
immutable=True,
)
return PublicCachePolicy(
ttl_seconds=env_nonnegative_int("PRISM_PUBLIC_CACHE_TTL_SECONDS", 5),
stale_while_revalidate_seconds=env_nonnegative_int(
"PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS",
30,
),
ttl_seconds=dynamic_ttl_seconds,
stale_while_revalidate_seconds=dynamic_stale_while_revalidate_seconds,
)


Expand Down
2 changes: 2 additions & 0 deletions tests/test_prism_compose_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ def test_prism_coordinator_gets_required_environment(self) -> None:
self.assertEqual(env["PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS"], "30")
self.assertEqual(env["PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS"], "300")
self.assertEqual(env["PRISM_PUBLIC_CONFIG_CACHE_STALE_WHILE_REVALIDATE_SECONDS"], "3600")
self.assertEqual(env["PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS"], "")
self.assertEqual(env["PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS"], "")
self.assertEqual(env["PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS"], "86400")
self.assertEqual(env["PRISM_PUBLIC_ARTIFACT_CACHE_STALE_WHILE_REVALIDATE_SECONDS"], "86400")
self.assertEqual(env["PRISM_PUBLIC_CACHE_MAX_ENTRIES"], "512")
Expand Down
34 changes: 34 additions & 0 deletions tests/test_prism_public_dashboard_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,8 @@ def test_public_api_cache_headers_use_route_specific_ttls(self) -> None:
os.environ,
{
"PRISM_PUBLIC_CONFIG_CACHE_TTL_SECONDS": "600",
"PRISM_PUBLIC_SETTLEMENT_CACHE_TTL_SECONDS": "15",
"PRISM_PUBLIC_SETTLEMENT_CACHE_STALE_WHILE_REVALIDATE_SECONDS": "10",
"PRISM_PUBLIC_ARTIFACT_CACHE_TTL_SECONDS": "7200",
},
clear=True,
Expand All @@ -854,6 +856,12 @@ def test_public_api_cache_headers_use_route_specific_ttls(self) -> None:
) as config_response:
json.loads(config_response.read())
config_cache = config_response.headers.get("CDN-Cache-Control")
with urllib.request.urlopen(
f"http://127.0.0.1:{server.server_port}/public/v1/blocks/{ledger.block_hash}/settlement-artifacts",
timeout=5,
) as settlement_response:
json.loads(settlement_response.read())
settlement_cache = settlement_response.headers.get("CDN-Cache-Control")
with urllib.request.urlopen(
f"http://127.0.0.1:{server.server_port}/public/v1/artifacts/{ledger.audit_bundle_sha256}",
timeout=5,
Expand All @@ -866,8 +874,34 @@ def test_public_api_cache_headers_use_route_specific_ttls(self) -> None:
thread.join(timeout=5)

self.assertEqual(config_cache, "public, max-age=600, stale-while-revalidate=3600")
self.assertEqual(settlement_cache, "public, max-age=15, stale-while-revalidate=10")
self.assertEqual(artifact_cache, "public, max-age=7200, stale-while-revalidate=86400, immutable")

def test_settlement_artifact_cache_default_stays_short_for_live_status(self) -> None:
path = f"/public/v1/blocks/{FakePublicLedger.block_hash}/settlement-artifacts"

with patch.dict(os.environ, {}, clear=True):
policy = public_api.public_cache_policy(path)

self.assertEqual(policy.ttl_seconds, 5)
self.assertEqual(policy.stale_while_revalidate_seconds, 30)

def test_settlement_artifact_cache_inherits_global_overrides(self) -> None:
path = f"/public/v1/blocks/{FakePublicLedger.block_hash}/settlement-artifacts"

with patch.dict(
os.environ,
{
"PRISM_PUBLIC_CACHE_TTL_SECONDS": "0",
"PRISM_PUBLIC_CACHE_STALE_WHILE_REVALIDATE_SECONDS": "7",
},
clear=True,
):
policy = public_api.public_cache_policy(path)

self.assertEqual(policy.ttl_seconds, 0)
self.assertEqual(policy.stale_while_revalidate_seconds, 7)

def test_public_api_errors_use_public_error_schema(self) -> None:
with self.assertRaises(urllib.error.HTTPError) as raised:
self.get_json("/public/v1/leaderboard?window=24h")
Expand Down