View Markdown

Operational limits and tuning

On this page

These are instance-wide startup settings. Change the environment or application YAML and restart that service; this change does not add runtime configuration mutation or tenant overrides. The defaults live in the bean binding declarations. The environment reference is generated from docker/.env.example and docker/learner.env.example; regression tests exercise binding and behavior.

The L1 environment names follow Spring Boot record binding: hyphens in a property name are removed, so max-weight-bytes is POINTSMAN_CACHE_L1_MAXWEIGHTBYTES. Use the exact spellings below.

L1 weight accounting#

text
effective budget = min(max-weight-bytes, max-size * min-entry-weight-bytes)
entry weight = max(min-entry-weight-bytes, entry-overhead-bytes + 2 * counted UTF-16 units)

The counted payload includes the key, content, reasoning, tool arguments and retained logprobs. Two bytes per UTF-16 unit is a representation constant; the metadata term is a tunable estimate. Caffeine takes a weight ceiling or an entry ceiling, so the per-entry minimum preserves an entry-equivalent bound with weight eviction. Defaults yield 40,960,000 bytes (39.1 MiB), because 10,000 * 4,096 is smaller than 64 MiB. This is approximate payload accounting, not a JVM RSS promise.

For an effective 128 MiB budget, both the byte ceiling and entry-equivalent ceiling must permit it:

dotenv
POINTSMAN_CACHE_L1_MAXWEIGHTBYTES=134217728
POINTSMAN_CACHE_L1_MAXSIZE=32768
POINTSMAN_CACHE_L1_MINENTRYWEIGHTBYTES=4096
POINTSMAN_CACHE_L1_ENTRYOVERHEADBYTES=1024

The admin effective-configuration view shows configured values and the derived effective budget. An entry heavier than the budget is not retained. Eviction is asynchronous; this is a steady-state cache budget. The L2 memory backend, active requests, SDKs and other caches have separate allocations.

Audit inventory#

All values in the following table are configurable without a source change. Units are part of the names. Invalid nonpositive values refuse startup; the L1 metadata overhead may be zero, and the minimum entry weight cannot exceed its byte ceiling. Large values still require operator sizing.

Environment variable Default Meaning / consequence
POINTSMAN_CACHE_L1_MAXSIZE 10000 Maximum entry-equivalent count; the per-entry weight floor enforces it.
POINTSMAN_CACHE_L1_TTLMINUTES 15 L1 TTL in minutes; refresh-on-hit determines sliding versus fixed expiry.
POINTSMAN_CACHE_L1_MAXWEIGHTBYTES 67108864 Estimated payload ceiling in bytes (64 MiB), not a measured JVM heap limit.
POINTSMAN_CACHE_L1_MINENTRYWEIGHTBYTES 4096 Minimum weight per entry. Effective budget=min(max-weight-bytes, max-size * this).
POINTSMAN_CACHE_L1_ENTRYOVERHEADBYTES 1024 Estimated metadata bytes added to key/text UTF-16 bytes; zero is allowed.
POINTSMAN_STREAMING_ASSEMBLY_MAX_CHARS 524288 Maximum cumulative UTF-16 characters accepted while assembling one provider stream; overflow aborts the stream.
POINTSMAN_STREAMING_ASSEMBLY_MAX_TOOL_CALLS 128 Maximum distinct tool-call indices assembled in one stream; overflow aborts the stream.
POINTSMAN_TOKENS_MAX_CONCURRENT_TOKENIZATIONS 2 Concurrent tokenizer-mode requests per instance. Up to POINTSMAN_TOKENS_MAX_WAITING_TOKENIZATIONS (16) further requests wait at most POINTSMAN_TOKENS_ADMISSION_WAIT_MS (25 ms) for a slot; beyond that, or after the wait, the request receives 503.
POINTSMAN_USAGE_JOURNAL_REPLAY_BATCH_SIZE 100 Maximum terminal journal records delivered per replay pass.
POINTSMAN_USAGE_JOURNAL_REPLAY_INTERVAL_MS 1000 Delay after each journal replay pass and before its first pass, milliseconds.
POINTSMAN_CACHE_CONTEXT_MEMO_MAX_SIZE 1000 Maximum conversation summaries held per instance; eviction only causes recomputation.
POINTSMAN_CACHE_CONTEXT_MEMO_TTL_MS 120000 Fixed conversation-summary memo lifetime, milliseconds.
POINTSMAN_CACHE_REDIS_SCAN_COUNT 500 Redis SCAN work hint for cache clearing; a hint, not a guaranteed batch size.
POINTSMAN_PROMETHEUS_CONNECT_TIMEOUT_MS 2000 Prometheus TCP connection timeout, milliseconds.
POINTSMAN_PROMETHEUS_READ_TIMEOUT_MS 3000 Prometheus response read timeout, milliseconds.
POINTSMAN_BANDIT_PARAMS_TIMEOUT_MS 2000 Background learner parameter-fetch timeout, milliseconds; independent of poll cadence.
POINTSMAN_BANDIT_REWARD_TIMEOUT_MULTIPLIER 4 Reward timeout is existing POINTSMAN_BANDIT_TIMEOUT_MS times this multiplier.
POINTSMAN_BANDIT_BREAKER_WINDOW_SIZE 5 Number of reward calls in the learner circuit-breaker sliding window.
POINTSMAN_BANDIT_BREAKER_HALF_OPEN_CALLS 3 Number of permitted learner reward probes in half-open state.
POINTSMAN_BANDIT_FEEDBACK_WINDOW_MAX_ENTRIES 100000 Local feedback throttle key capacity; eviction can reset a local-only throttle. Use Redis for fleet limits.
POINTSMAN_AUTH_LOGIN_THROTTLE_MAX_ENTRIES 100000 Local failed-login username capacity; eviction forgets that username window. Size for exposed account cardinality.
POINTSMAN_ROUTING_AFFINITY_MAX_ENTRIES 32768 Maximum local conversation pins; at capacity new pins are declined after expired pins are removed.
POINTSMAN_ROUTING_AFFINITY_TTL_MS 300000 Sliding conversation affinity lifetime, milliseconds; this does not extend provider-side prompt cache lifetime.
POINTSMAN_PROVIDERS_MODEL_CACHE_MAX_ENTRIES 64 Maximum dynamic provider/credential client entries; new entries at capacity are refused.
POINTSMAN_PROVIDERS_CLOSE_QUEUE_CAPACITY 100 Queued retired clients awaiting closure; saturation runs the close on the caller, never drops it.
POINTSMAN_COST_SAMPLER_MAX_SCOPES 10000 In-memory cost-learning states (one per tenant and objective: cost group, or per organisation and selection: cost profile). Beyond it the least recently used state is dropped, which returns it to declared prices; counted as pointsman_cost_sampler_evictions_total. Measured 17 October 2026 at the cap with 8 arms each: about 10 MB of heap (about 1 KB per state), a choice in 0.8 us (p99 4.4 us); under continuous churn of 100,000 new states the heap stayed at 12 MB and a new state took p99 1.5 ms (eviction runs in batches of 1%).
POINTSMAN_COST_SAMPLER_MAX_ARMS_PER_SCOPE 64 Members learned per state. A member beyond it is ranked from its declared price and not learned; counted as kind=arm_cap.
POINTSMAN_COST_SAMPLER_MAX_LABEL_SCOPES 100000 Quality-label states (feedback_floor). Never evicted - forgetting a label could re-admit an excluded model - so a NEW organisation and profile beyond it records no labels (counted as kind=label_scope_refused, warned once). Measured 17 October 2026: about 0.7 KB of heap per label state with four labelled models, so the full 100,000 is about 73 MB; on a small heap, lower it to the number of organisations times cost profiles you expect.

Learner settings: BANDIT_DB_TIMEOUT_SECS=5.0 bounds SQLite lock waiting, and BANDIT_PENDING_SWEEP_INTERVAL_SECS=60.0 controls cleanup of the dormant decide endpoint's pending state. They are typed and range-validated in LearnerSettings; the live posterior algorithm is unchanged.

Learning capacity (measured 17 October 2026)#

The learner (policy service, one worker by invariant) applies rewards one at a time under one lock, each committed to SQLite. On the 16-core validation host an update took about 6 ms (p99 about 10 ms) whether a key had 2 or 64 arms and whether the store held 100 or 5,000 keys, so it sustains about 150 rewards per second. Above that rate the gateway's reward queue (POINTSMAN_BANDIT_REWARD_QUEUE_SIZE, 1,000) fills and drops the newest rewards, counted as gateway_bandit_rewards_dropped_total{reason="overflow"}; routing is not affected. At 5,000 keys the published parameter document was 1.9 MB (built in 30 ms) and a restart loaded the store in 0.17 s.

Learning has a cumulative horizon. The learner keeps every reward event for idempotent retries and revisions, up to BANDIT_REWARD_EVENT_LIMIT (1,000,000). At the limit it refuses new rewards (503) rather than evict that history, and balanced bandit groups stop learning - they keep routing on the last posteriors. At a sustained R rewards per second the limit is reached after 1,000,000 / R seconds: about 2.3 days at 5 per second, 11.6 days at 1 per second. The limit can be raised to 100,000,000; the store grew by roughly 600 bytes per event in the measurement. There is no archival or checkpoint tool yet, so plan the limit against the expected reward rate. objective: cost groups learn in the gateway and do not use this store.

The gateway's cost sampler is bounded by the three POINTSMAN_COST_SAMPLER_* settings above, and starts again from declared prices after a restart.

Constants deliberately retained#

The audit covers resource capacities, client timeout budgets, cache lifetimes, batching and worker admission. Existing configured values (request body ceiling, output cap, retry timing, retention, queue depth and posterior parameters) retain their existing configuration paths.

Protocol limits (four stop sequences, JavaScript-safe feedback revisions), encoding widths, hash constants, feature-vector dimensions, schema identifiers and numerical guards are not deployment knobs. The tokenizer's named chat-framing allowance remains an estimate of a provider format, not an operator-controlled admission limit. Redis SCAN count is configurable, but Redis may return more or fewer entries than requested. Executor worker count and one-chunk stream prefetch preserve execution and backpressure semantics; the closer's idle timeout has no effect while its core thread is retained. Admin page/range ceilings and bounded diff rendering remain API complexity limits. Changing those contracts warrants API review rather than silently allowing deployment-specific response shapes.

No claim is made that every numeric literal should become a setting. A future operational limit must have an explicit unit, documented default, validation, saturation behavior and a test proving its configured value reaches the consumer. Preserve immutable protocol and algorithm constants by name.

TCP admission before the request gate#

server.tomcat.accept-count defaults to 1024 queued TCP connections, configurable through Spring Boot's SERVER_TOMCAT_ACCEPTCOUNT environment variable. Set a positive integer and restart the gateway. The operating system may cap the effective listen backlog; inspect it on Linux with ss -lnt and check net.core.somaxconn.

This queue is separate from pointsman.intake.max-in-flight, which bounds admitted /v1/ requests. Increasing the TCP backlog does not grant more application permits. It lets short connection bursts reach the request gate instead of waiting for TCP retransmission after the listen queue overflows. It is still finite and cannot make unbounded connection floods or exhausted CPU capacity safe.

Tomcat 10.1 closes HTTP/1.1 connections after a 503 response, so repeated overload refusals create connection churn. On the CPX62 diagnostic workload (200 simultaneous requests, 32 application permits), the inherited backlog of 100 produced approximately one-second TCP retries. An otherwise identical 1024-backlog arm removed captured handshake retries and brought refusal p99 below the unchanged 100-ms gate. This is a measured diagnostic profile, not an independent-generator capacity guarantee.

Measure both client latency and server processing time. A fast filter cannot reject a request that has not yet crossed the TCP connection queue. Keep ingress/network latency, HTTP protocol and connection reuse in the report. See the overload investigation.

Pointsman v1 candidate documentation Source: 0e09f5e7

Search guides, reference pages and architecture.

Documentation

Open full-size image