Skip to content
Merged
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
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

All notable changes to flAPI are documented here. Versions follow `vYY.MM.DD` (the date the binary set was cut). Earlier history is in the git log.

## Unreleased

### Health checks during cache warmup

- The HTTP listener now opens before cache warmup completes, so platforms can connect during long
startup cache builds.
- Added always-on `GET /health/live` for liveness and `GET /health` for readiness. `/health`
returns `503` while caches are still starting or degraded.
- Cache-enabled data endpoints now return `503` with `Retry-After: 5` while their cache is starting
or failed, instead of risking a `200` response from a half-built cache.
- Cache warmup failures are captured per endpoint and surfaced in health output; warmup continues
to later caches.
- Concurrent refreshes for the same DuckLake cache table are suppressed so scheduler refreshes do
not collide with startup warmup.

### Build correctness

- `CROW_ENABLE_COMPRESSION` is now defined once for every translation unit via CMake instead of by
three headers. The macro adds a member to `crow::response`, so a translation unit that reached
`<crow.h>` through a different include order saw a different layout — an ODR violation that made
`response::is_completed()` read an unrelated byte across the library/test boundary.
- The unit tests that use the `#define private public` access hack now include `<crow.h>` before it,
so crow is never parsed with rewritten access specifiers.

## v26.05.18 — Prepared-statement coverage swept across every code path

Follow-up to v26.05.17. After v26.05.17 shipped, an internal audit found that the prepared-statement path was only wired into the GET endpoint executor — POST/PUT/PATCH writes and the Arrow-streaming endpoint still rendered Mustache templates as strings. This release closes that gap.
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ set(CROW_ENABLE_COMPRESSION ON)
# OpenSSL is already a required dependency; turning this on has no runtime
# cost when `enforce-https.enabled` is false.
add_compile_definitions(CROW_ENABLE_SSL)
# CROW_ENABLE_COMPRESSION changes crow::response's layout. It must be defined
# for every translation unit, not per-header, or TUs that reach <crow.h> by a
# different include order disagree about the layout (an ODR violation that
# silently corrupts response state across the lib/test boundary).
add_compile_definitions(CROW_ENABLE_COMPRESSION)

# Compiler flags
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,16 @@ integration-test-ci: release integration-test-setup
$$FLAPI_BIN --config examples/flapi.yaml --log-level info --config-service --config-service-token test-token & \
SERVER_PID=$$!; \
echo "Server started with PID: $$SERVER_PID"; \
sleep 5; \
python3 -c 'import sys,time,urllib.request; url="http://localhost:8080/health"; last=None; \
for i in range(30): \
try: \
r=urllib.request.urlopen(url, timeout=5); \
code=r.getcode(); \
if code == 200: print("Server healthy at " + url); sys.exit(0); \
last="status " + str(code); \
except Exception as e: last=str(e); \
print("Waiting for server readiness (attempt %d/30): %s" % (i + 1, last)); time.sleep(1); \
raise SystemExit("Server at " + url + " failed health check after 30 attempts")' || { kill $$SERVER_PID 2>/dev/null || true; wait $$SERVER_PID 2>/dev/null || true; exit 1; }; \
echo "Running integration tests..."; \
cd test/integration && \
if command -v uv >/dev/null 2>&1; then \
Expand Down
61 changes: 57 additions & 4 deletions docs/CLI_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ This document provides a complete reference for the `flapi` server executable's
- [Development Mode](#development-mode)
- [Production Mode](#production-mode)
- [CI/CD Validation](#cicd-validation)
6. [Signal Handling](#6-signal-handling)
7. [Exit Codes](#7-exit-codes)
6. [Runtime Health Endpoints](#6-runtime-health-endpoints)
7. [Signal Handling](#7-signal-handling)
8. [Exit Codes](#8-exit-codes)
- [Related Documentation](#related-documentation)

---
Expand Down Expand Up @@ -636,6 +637,15 @@ See [Configuration Reference - Environment Variables](./CONFIG_REFERENCE.md#10-e
--log-level warning
```

For platforms with short startup health-check windows, point the platform liveness check at:

```text
/health/live
```

Use `/health` when the platform should wait until cache warmup is complete before marking the
deployment ready for traffic.

### CI/CD Validation

```bash
Expand All @@ -654,7 +664,50 @@ fi

---

## 6. Signal Handling
## 6. Runtime Health Endpoints

flAPI always registers two unauthenticated health endpoints. They do not require
`--config-service`.

| Endpoint | Purpose | Response |
|----------|---------|----------|
| `GET /health/live` | Liveness: process is up and the listener is accepting connections | `200 {"status":"live"}` |
| `GET /health` | Readiness: cache-enabled endpoints are ready to serve | `200` when ready, `503` while starting or degraded |

During cache warmup, `/health/live` returns `200` as soon as the server is listening, while
`/health` returns:

```json
{
"status": "starting",
"caches": {"total": 4, "ready": 1, "failed": 0},
"pending": [{"schema": "cache", "table": "customers_cache"}],
"uptime_s": 7
}
```

If a cache warmup fails, `/health` returns `503` with `"status": "degraded"` and a `failed` list
including the table name and error.

Requests to a cached data endpoint while its cache is starting or failed return `503 Service
Unavailable` with `Retry-After: 5` and a JSON body containing `"error": "cache_warming"`. flAPI
does not serve partial or empty results from a half-built cache.

**AWS App Runner example:**

```yaml
HealthCheckConfiguration:
Protocol: HTTP
Path: /health/live
Interval: 5
Timeout: 2
HealthyThreshold: 1
UnhealthyThreshold: 5
```

---

## 7. Signal Handling

| Signal | Behavior |
|--------|----------|
Expand All @@ -679,7 +732,7 @@ On receiving a shutdown signal, the server:

---

## 7. Exit Codes
## 8. Exit Codes

| Code | Description |
|------|-------------|
Expand Down
33 changes: 30 additions & 3 deletions docs/CONFIG_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,34 @@ cache:
schedule: 5m
```

### 6.2 Refresh Modes
### 6.2 Cache Readiness During Warmup

At startup, flAPI opens the HTTP listener before cache warmup finishes. Cache-enabled endpoints are
not allowed to serve until their configured cache table is fully built.

While a cache is still building, requests to that endpoint return:

```http
503 Service Unavailable
Retry-After: 5
Content-Type: application/json
```

```json
{
"error": "cache_warming",
"message": "Cache for this endpoint is still being built",
"table": "customers_cache"
}
```

If warmup fails, the endpoint continues to return `503` and includes the failure detail. This is
intentional: a cached endpoint must never return `200` with empty or partial results from a
half-built cache.

Endpoints without a `cache:` block are not gated by cache readiness.

### 6.3 Refresh Modes

**Full Refresh (Default):**

Expand Down Expand Up @@ -1217,7 +1244,7 @@ cache:
type: timestamp
```

### 6.3 Retention Policies
### 6.4 Retention Policies

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
Expand All @@ -1244,7 +1271,7 @@ cache:
delete-handling: soft
```

### 6.4 Cache Template Variables
### 6.5 Cache Template Variables

Special variables available in cache-enabled SQL templates:

Expand Down
Loading
Loading