#2500·floci

[BUG] cwlogs: every PutLogEvents batch rewrites the entire events store — quadratic write amplification can saturate the host disk

Author: hisham-coderCreated Aug 23, 2026Updated Sep 17, 2026
Labelsbugcloudwatchhas-pr

What happens CloudWatch Logs persistence serializes the entire events store to data/cwlogs-events.json (write to .tmp, rename) on every PutLogEvents batch. The write cost of one incoming log line is therefore proportional to all events ever stored, and total disk traffic grows quadratically with event count.

Why it matters Any steady log producer eventually saturates the host's disk. Observed on a small real deployment (Floci 1.7.0, community edition, two ECS-emulated apps shipping container logs via the awslogs driver):

  • ~62k events accumulated over ~20h from one chatty Celery worker (-l info, ~1 line/s)
  • store grew to 41 MB, rewritten more than once per second
  • the application process wrote a sustained 54–74 MB/s (measured via /proc/<pid>/io), with cwlogs-events.json.tmp its only open data file
  • on an HDD-backed ZFS host this saturated the pool: 4–14 s transaction syncs, other containers' processes in D-state, docker builds 8× slower, and eventually the Floci data plane itself browned out

The producer writing ~2 KB/s of logs caused ~70 MB/s of disk writes — a ~35,000× amplification at this store size, and it only grows.

Reproduce (Floci 1.7.0)

bash
aws --endpoint-url http://localhost:4566 logs create-log-group --log-group-name /t
aws --endpoint-url http://localhost:4566 logs create-log-stream --log-group-name /t --log-stream-name s
# feed it a modest stream and watch the process's write_bytes explode as the store grows
for i in $(seq 1 100000); do
  aws --endpoint-url http://localhost:4566 logs put-log-events --log-group-name /t --log-stream-name s \
    --log-events timestamp=$(date +%s%3N),message="line $i" >/dev/null
done
# watch: awk '/write_bytes/{print $2}' /proc/<floci-pid>/io   (delta per second ≈ current store size × batch rate)

Expected Ingesting one log batch should cost O(batch), not O(all events ever). Any of:

  • append-only journal (JSONL) with periodic compaction,
  • per-stream files so a batch touches only its stream,
  • debounced/periodic persistence instead of per-batch,
  • and/or honoring PutRetentionPolicy + a size cap so the store cannot grow unbounded.

Workaround we use meanwhile: run app loggers at warning, and periodically DeleteLogGroup (or stop Floci and remove data/cwlogs-events.json).