Skip to content

v2.1.0

Choose a tag to compare

@jdwyah jdwyah released this 23 Oct 15:14
· 5 commits to main since this release
6a6f49e

Upgrade notes

  • UPGRADE: a forked child that cannot load a config now raises under the
    default on_init_failure="fail" (minor, qfg-6vno.23).
    A forked child whose lazy re-initialization fails
    under the default on_init_failure="fail" now raises, matching a fresh boot and sdk-ruby
    (init-failure design B.3 "Fork"; qfg-6vno.23). Before, a recycled worker
    that came up during a delivery outage blocked up to init_timeout_ms on its
    first getter and then silently served code defaults. Now that first content
    read (get, get_string, enabled, keys, ...) raises QuonfigInitError,
    and every later one raises the same error without a new fetch until the
    background update channel installs a config. *_details getters return
    reason="ERROR", error_code="PROVIDER_NOT_READY" instead of raising;
    should_log, with_context, refresh and the health accessors never
    raise it. continue and wait are unchanged, an explicit init() in the
    child still raises as before, and the parent is never touched. This is a
    behavior change on the default mode, approved as minor under Decision 1
    (qfg-6vno.16).
    Decided by Jeff 2026-10-22; this changes init-failure handling, not who
    matches a rule, so no prod corpus scan applies.

  • UPGRADE: size caps on delivery payloads (minor, qfg-6vno.20.4; supervisor
    contract "Rule — size caps", qfg-6vno.8).
    One SSE event is capped at 4 MiB
    (4,194,304 bytes), counting every line of the event, so a multi-line data:
    sum is capped too. An oversized event is not a frame: the connection is a
    failed attempt (backoff grows, never connected, the fallback poller takes
    over), and a later keepalive-only connection does not report connected
    until an envelope installs. A 200 body from /api/v2/configs is capped at
    16 MiB (16,777,216 bytes): a Content-Length over it fails before reading,
    otherwise reading stops at the cap + 1, and the leg fails like a 5xx
    (nothing installed, no ETag stored). Each logs one WARN per oversized
    episode naming the source host and the cap; repeats are DEBUG. The caps are
    constants, not options.

Changed (minor)

  • A raise from a user callback is a callback error, not a worker restart
    (minor, qfg-6vno.29; supervisor contract Test 5). A caught raise from
    on_config_update or on_sse_connection_state_change is counted in the
    test-only _chaos_diagnostics()["callback_errors"]
    (quonfig_sdk_callback_errors_total{callback}, callback in
    on_config_update / on_connection_state_change) and no longer bumps the
    Layer 1 restart counter. Both are logged at ERROR with the traceback; an
    on_sse_connection_state_change raise was logged at WARNING without one.
    The diagnostics hook is not public API.

Fixed

  • The SSE parser no longer rescans the carried unterminated tail on every
    chunk, so one large event parses in linear time instead of quadratic
    (qfg-6vno.20.4).

  • Getters no longer raise on malformed input (minor, error-path behavior
    change, qfg-6vno.14). A context namespace that is not a mapping (e.g.
    contexts={"user": None}) is ignored instead of raising TypeError from
    every getter; a config whose criterion has a non-string propertyName is
    skipped at decode with a WARN (like a malformed weight) instead of raising
    AttributeError for that key; and a telemetry failure (e.g. a context key
    whose __str__ raises) can no longer escape a plain getter. Plain getters
    return the default, *_details keep returning their normal result.

Tests

  • The chaos harness reads every asserted value from the SDK: connection_state(),
    fallback_poller_active() and a test-only _chaos_diagnostics() hook (SSE
    connect attempts, Layer 1 restarts, SSE worker liveness; not public API). It no
    longer invents a reconnecting state, reports the layer-2 restart counter
    SKIPPED (the poller has no restart supervisor), fails on an unknown inject or
    process action, and implements the scenario 00 init-failure vocabulary. Chaos
    and failover pins move to ITD v2026.10.21.1, and CI fails when the three ITD
    refs differ (qfg-6vno.5, qfg-6vno.7).
  • The chaos probe reads sdkMetric('quonfig_sdk_callback_errors_total', callback=...) from the SDK, and all ITD pins move to v2026.10.22.2 (size
    caps Test 10, callback-error Test 5, scenario 10 on the new counter, regex
    corpus cases) (qfg-6vno.29).