Testing Agent Memory on microVMs
Let's demonstrate how Polign handles a cold start on Micro VMs.
In my last post about agent memory, I wrote about how agents are moving to the edge, and how memory needs to move with them without picking up any side effects along the way. I wanted to take that a step further and show how Polign handles a cold start on micro VMs, and how it can be deployed at short notice.
A quick background on Polign, in case you are reading about it for the first time: Polign is a lightweight, stateless agent memory and vector database that keeps its durable state in object storage. With an S3-backed store, a write goes into the bucket's write-ahead log (WAL) before it is acknowledged. On a fresh server start, the Polign server reads the tail of the WAL along with the objects it needs from the bucket.
I ran a test on a small set of objects across six cold runs on AWS Lambda. This is the sequence I used:
- Write the preference in environment A.
- Read it from a fresh environment B.
- Have ten fresh readers query the same unchanged data in S3.
The setup for this exercise
- ARM64 Lambda custom runtime in
us-west-1. - 512 MiB of memory and an S3 bucket in the same region, with the segment cache capped at 64 MiB.
- Polign server zipped bundle (14.3 MiB).
- Invocation requests to Polign's local HTTP endpoint.
- Background persistor turned off, so the WAL entries are read directly instead of from segment objects.
Command to run
GOMEMLIMIT=384MiB ./polign-server \
-store s3://YOUR-BUCKET/microvm-test \
-persist=false \
-maintain 0 \
-http 127.0.0.1:23000 \
-grpc 127.0.0.1:23001 \
-hot-max 0 \
-disk-cache-bytes 0 \
-segment-cache-bytes 67108864 \
-max-resident-searchers 64
The setup ensures that it's a cold start for the Polign server. The bucket still contained only the WAL and schema objects. The new process had recovered the record from the S3 log.
Polign relies heavily on S3's strong read-after-write consistency to find the logs. A fresh Polign node captures the highest visible log sequence in each partition and replays through those positions before accepting queries.
Tracing the timing profiles
The experiment ran over six cold starts to identify the initialization time and queries with warm invocations. All ten readers found the preference written earlier with environment A. Nothing was being written during the run, so this says nothing about write contention or readers keeping up with active ingestion.
Every result is for one stored record. This measures initialization and a small query on Lambda.
| Run | Result | Notes |
|---|---|---|
| Cold initialization | 645 ms p50 562 to 677 ms range |
Six forced replacements; AWS Init Duration, including runtime and Polign startup. |
| Polign healthy | 596 ms p50 527 to 630 ms range |
Process start to /healthz returning 200, including startup WAL replay. |
| First query after replacement | 23.0 ms p50 19.6 to 31.5 ms range |
Six runs, querying the same record after startup replayed the retained S3 WAL. |
| Cold invocation, including init | 669 ms p50 587 to 711 ms range |
AWS billed duration for the same six cold invocations; excludes the caller's network round trip. |
| 100 warm queries | 23.7 ms p50 30.0 ms p95 43.4 ms p99 |
One invocation, sequential local HTTP requests. Minimum 19.9 ms; maximum 57.0 ms. |
| Memory | 45.5 MiB p50 RSS 71 to 74 MB AWS peak |
Runtime adapter and Polign together; six cold runs on a 512 MiB allocation. |
| 10 concurrent cold readers | 10/10 succeeded 22.2 ms query p50 30.4 ms max |
Ten distinct runtime instance IDs; one existing record, no concurrent writes. Polign-ready p50 was 570 ms. |
For this record, cold and warm query times are close. The time (645 ms) was spent initializing the runtime and replaying the WAL entries.
Two writers, one log
To test concurrent writes, I ran two writers against one shared WAL:
- Each writer attempted 150 individual inserts of synthetic three-dimensional vectors and 15 writes to a shared record, with one request in flight at a time.
- Both writers wrote to the same collection and S3 prefix.
- A third Lambda function queried the collection throughout ingestion.
In a separate restart test, A's Polign process was killed while writer B continued writing. The HTTP 500 errors came from exhausting the WAL's conflict limits.
| Run | Successful writes | Successful write latency |
|---|---|---|
| One writer, two rounds | 330 / 330 | 22.0 ms p50 29.3 ms p95 146 ms max |
| Two writers, first pass Three rounds |
989 / 990 One HTTP 500 after 3.40 s |
21.1 ms p50 28.7 ms p95 3,410 ms max |
| Two writers, repeat Three rounds |
989 / 990 One HTTP 500 after 3.29 s |
21.2 ms p50 28.4 ms p95 2,717 ms max |
| Two writers, restart A during ingestion, two rounds | 660 / 660 | 21.0 ms p50 27.0 ms p95 1,411 ms max |
The goal of this exercise, and the encouraging result for me is that an agent's memory can outlive its process. In these tests, a replacement process recovered the acknowledged writes from S3. That makes Polign a promising fit for agents running in short-lived microVMs. Each session can conclude safely and be replaced without losing context.