Non-Blocking Startup

Applications can start serving traffic immediately while synchronization occurs in the background.

Introduction

SOASAP separates application startup from configuration synchronization. Applications should not delay readiness because of network communication with a feature flag service — flag configuration is not a bootstrap gate for accepting traffic.

SOASAP SDKs restore cached snapshots during client construction and synchronize over SSE in the background. Applications can begin evaluating flags and serving requests before the first SSE payload arrives or before SOASAP Cloud responds.

Non-blocking startup is intentional. The host starts, flags are readable from cache or defaults, readiness probes can pass, and synchronization catches up independently.

Why non-blocking startup exists

Blocking startup on feature flag synchronization couples deploy velocity and autoscaling to control-plane latency. The blocking model looks like this:

Blocking startup has predictable consequences:

  • Slower deployments — each new instance waits on network I/O before accepting traffic
  • Slower autoscaling — scale-out events stall until flag sync completes on every new pod
  • Delayed readiness probes — Kubernetes and load balancers mark instances not ready during sync
  • Longer cold starts — serverless and container cold starts include control-plane round trips
  • Additional startup failure modes — DNS, TLS, or SOASAP Cloud errors block application boot

SOASAP avoids this architecture. Cache restoration and code defaults make flags available at construction time; SSE connects asynchronously without blocking StartAsync, module initialization, or the first HTTP request.

Startup flow

  1. Application start — the host, container, or app shell initializes; the SDK client is constructed
  2. Restore cache — the SDK loads the persisted snapshot from disk into memory when a valid cache file exists (synchronous on most server platforms)
  3. Flags available — getters return cached snapshot values; code defaults apply for missing keys
  4. Application ready — the process accepts traffic; readiness probes can succeed without waiting for SSE
  5. Connect SSE — the background worker opens a long-lived connection to SOASAP Cloud (when preload is enabled or on first lazy evaluation)
  6. Receive updates — SSE delivers configuration events; the SDK atomically refreshes the in-memory snapshot and debounces cache writes

Applications can evaluate flags before synchronization completes. The first SSE payload may arrive seconds after boot on healthy networks — traffic served during that window uses cache and defaults, not blocking waits.

Preload

Most SDKs support preload — an option that starts the SSE background worker during initialization without awaiting network I/O. Preload is recommended for production servers, workers, and mobile apps.

Examples:

.NET

builder.Services
    .AddSoasap(apiKey)
    .PreloadFlags();

PreloadFlags() registers a hosted service that calls EnsureWorkerStarted() and returns immediately — no network await in StartAsync. Alias: UseBackgroundWorker().

Node.js

import { createSoasapClient } from '@soasap-com/node-sdk';

const flags = createSoasapClient({
    apiKey: process.env.SOASAP_API_KEY,
    preload: true,
});

Python, React, Angular, React Native, Kotlin

Pass preload: true or preloadFlags = true in client or provider options. See platform SDK documentation for exact syntax.

Preload initializes synchronization infrastructure early while preserving non-blocking startup. It does not block the constructor, host startup, or render cycle — the SSE worker runs in the background from the first moment the client exists.

Lazy mode (preload disabled): the background worker starts on the first flag read instead of at construction. Evaluation still uses cache and defaults immediately; only the timing of SSE connection differs. Prefer preload for predictable sync after deploy.

See Preload Flags (.NET).

First startup vs restart

First startup

No persistent cache exists on the host for this API key binding.

  • Getters return explicit code defaults for all flag keys
  • The application is still ready to serve traffic — non-blocking startup applies
  • SSE synchronizes in the background and builds the first in-memory snapshot
  • After validation, the SDK persists the first cache file for subsequent restarts

Restart

A valid cache file exists from a prior successful synchronization.

  • The SDK restores the cached snapshot into memory during construction
  • Flags are immediately available from the last synchronized state
  • SSE connects in the background and applies any changes since the cache was written
  • Rolling deploys and pod restarts benefit from this path on every restart after first sync

Relationship to persistent cache

Persistent Cache enables non-blocking startup across restarts. Without cache, first-start behavior applies on every cold boot until SSE delivers the first payload — still non-blocking, but only defaults are available until sync completes.

Without persistent cache (every cold start)

The process is still non-blocking — it does not wait before accepting traffic. However, only code defaults are available until the first SSE payload arrives.

With persistent cache (restart)

The last synchronized configuration is available before SSE connects. Persistent cache converts restart behavior from defaults-first to snapshot-first — the primary operational benefit for deployments and autoscaling.

Health checks and readiness

Applications should not fail readiness checks while waiting for synchronization. Flag evaluation remains available through cache restoration, the in-memory snapshot, and explicit code defaults — independent of SSE connection state at boot.

This matters for platforms that gate traffic on readiness:

  • KubernetesreadinessProbe should reflect application dependency health, not SOASAP sync completion; liveness must not require live SSE
  • Container platforms — ECS, Cloud Run, and App Service health endpoints should not await control-plane connectivity
  • Autoscaling systems — new instances join the pool when the app is ready, not when feature flags finish syncing

Monitor synchronization separately — via OnError handlers, metrics, or alerts on prolonged SSE disconnect. A ready pod may serve slightly stale flag values for seconds after boot until SSE catch-up completes.

Autoscaling and deployments

Non-blocking startup improves operational velocity when instances are created frequently:

  • Rolling deployments — new pods serve traffic with cached snapshots while SSE reconnects
  • Blue/green deployments — green instances pass health checks before sync completes
  • Kubernetes pod startup — scale-out adds capacity without per-pod sync waits
  • Serverless cold starts — function instances evaluate from cache or defaults without blocking init on network
  • Horizontal scaling events — traffic shifts to new instances immediately; sync is background work

Combined with persistent cache, each new instance in a deployment inherits the last synchronized state from disk — not an empty snapshot waiting on the first network round trip.

What happens if synchronization fails?

Application startup continues regardless of synchronization outcome. If SOASAP Cloud is unreachable at boot, the SDK restores cache (when present), marks the process ready for flag evaluation, and retries SSE connection with exponential backoff in the background.

Evaluation continues from cache, memory, and defaults — Offline Operation applies from the first moment sync fails. Synchronization errors are reported through error handlers; they do not fail host startup or throw from getters.

Startup and synchronization are intentionally independent. A sync failure is a background operational concern — not a bootstrap failure. This prevents transient control-plane issues from blocking deploys and scale events.

Common misconceptions

"Applications must wait for SOASAP before starting"

False. The host starts immediately. Cache and defaults make flags readable before SSE connects.

"Health checks should fail until synchronization completes"

False. Readiness should reflect application health, not SOASAP sync status. Monitor synchronization separately.

"Preload blocks startup"

False. Preload starts the background SSE worker without awaiting network I/O. Host StartAsync and client construction return immediately.

"Applications cannot evaluate flags before synchronization"

False. Cached snapshots and explicit defaults allow immediate evaluation. The first SSE payload updates the snapshot; it is not a prerequisite for the first read.

"First-start instances behave like restarts"

False. Without a cache file, only code defaults are available until the first successful sync. Plan safe defaults for first-deploy scenarios.

Production recommendations

  • Enable preload where supported — start SSE during initialization for predictable sync timing; avoid lazy mode in production servers unless you have a specific reason.
  • Enable persistent cache — ensure writable cache storage so restarts and scale-out events restore snapshots instead of defaults-only state.
  • Always provide explicit defaults — first start, missing keys, and sync delay all fall back to code defaults; define safe production behavior explicitly.
  • Do not block readiness on synchronization — readiness probes should not await SOASAP connectivity; decouple app health from configuration freshness.
  • Monitor synchronization health separately — alert on SSE disconnect duration and error handler frequency; ready instances may serve stale flags briefly after boot.
  • Test startup behavior during outages — block outbound SOASAP traffic during deploy drills; verify instances become ready and evaluate from cache or defaults without boot failure.

Related documentation