Stateful Counter
Preserve in-memory and durable volume state across actor suspend and resume cycles.
This directory contains a demo of a stateful counter application running on Agent Substrate.
It deploys a simple Go HTTP server (counter.go) that increments two counters on every request — one in process memory, one in a file on a durable volume — and preserves both across suspends and resumes: the template’s MEMORY preferredFidelity snapshot captures process memory alongside the durable volumes, so the in-memory count continues from where it left off. (With a VOLUMES fidelity, only the durable-volume counter would survive and the in-memory counter would restart from a cold boot. ROOTFS also keeps each container’s rootfs writes, but not process memory, so the in-memory counter would restart under it too.)
The demo uses two kinds of resources: the WorkerPool is a Kubernetes CRD, while the actor template is a Substrate ActorTemplate resource — an ateapi.ActorTemplate living in an atespace rather than a Kubernetes namespace, managed through the ate API with kubectl ate.
Prerequisites
- A k8s cluster with Agent Substrate installed as in the Quickstart
(with
ATE_INSTALL_KIND=trueexported in your shell) or GKE Quickstart. - A snapshot location set at install (
SNAPSHOT_LOCATION). The demo stores its snapshots there.
How to Run on Agent Substrate
The Quickstart already deploys this
demo, creates my-counter-1 and starts its proxy on port 8000. If you ran
it, skip to How to Use.
1. Build and Deploy
Note
Do not manually edit demos/counter/counter.yaml.tmpl or
demos/counter/counter-template.yaml.tmpl. ate-setup fills in their
${...} placeholders during deployment.
Installation on MicroVM
The in-RAM-counter suspend/resume-continuity demo be run on the microVM
sandbox class (ateworker-microvm: a Kata guest on Cloud Hypervisor).
demos/counter/counter-microvm.yaml.tmpl— theWorkerPoolfor the microVM sandbox class.demos/counter/counter-microvm-template.yaml.tmpl— thecounter-microvmactor template, created through the ate API in theate-demo-counter-microvmatespace.hack/run-microvm-demo.sh— one-shot bring-up that builds the microVM worker image, stages the guest assets, deploys the control plane, and applies the manifests above. Like the other hack scripts it reads.ate-dev-env.shfor GKE; use the kind wrapper for a local cluster.
Run it and follow the printed next steps:
# GKE (uses .ate-dev-env.sh, uploads assets to GCS):
./hack/run-microvm-demo.sh
# local kind (local registry + in-cluster rustfs):
KIND_CLUSTER_NAME=<cluster> ./hack/run-microvm-demo-kind.sh
On a cluster that already has the microVM deps (hack/install-microvm-deps.sh),
deploy directly instead:
go run ./cmd/ate-setup deploy demo counter-microvm
Installation on GVisor
Use `ate-setup` to build the image and deploy the demo to your cluster:go run ./cmd/ate-setup deploy demo counter
To enable validation of reading from an external volume (e.g.
/external-data/test.txt), run:
go run ./cmd/ate-setup deploy demo counter --with-external-volume --preview=ExternalVolumes
This command will:
- Build the counter server image using
ko. - Apply
counter.yaml.tmpl: theate-demo-counternamespace and thecounterWorkerPool, then wait for the worker rollout. - Create the
ate-demo-counteratespace. - Create the
counteractor template through the ate API (kubectl ate create actor-template) fromcounter-template.yaml.tmpl. The manifest is the message’s protojson form — the same shapekubectl ate get actor-template -o yamlprints inside itsactorTemplateslist. - Wait until the template’s golden snapshot is ready.
Inspect the deployed template with:
kubectl ate get actor-templates -a ate-demo-counter
kubectl ate get actor-template counter -a ate-demo-counter -o yaml
2. Create a Counter Actor
Create the counter actor with a chosen ID (e.g., my-counter-1) using --template (the template’s name, resolved in the actor’s atespace — so the actor lives in the demo’s atespace, which the deploy step already created):
Creation on MicroVM
# Install the CLI as a kubectl plugin if not already installed
go install ./cmd/kubectl-ate
# Create the actor from the counter template.
kubectl ate create actor my-counter-microvm-1 -a ate-demo-counter-microvm --template counter-microvm
Creation on GVisor
# Install the CLI as a kubectl plugin if not already installed
go install ./cmd/kubectl-ate
# Create the actor from the counter template.
kubectl ate create actor my-counter-1 -a ate-demo-counter --template counter
3. Proxy to the Actor
Start a local proxy that authenticates with atenet-router and forwards local port 8000 to port 80 on the actor:
Proxy on MicroVM
kubectl ate proxy actor my-counter-microvm-1 -a ate-demo-counter-microvm
Proxy on GVisor
kubectl ate proxy actor my-counter-1 -a ate-demo-counter
How to Use
When you send an HTTP request through the proxy, atenet-router automatically activates (resumes) the actor onto an available worker pod and routes the traffic.
- In a separate terminal, send an HTTP POST request to increment the counter:
curl -X POST http://localhost:8000
- Verify that the actor is now in a
RUNNINGstate and assigned to a worker pod:
Verify on MicroVM
kubectl ate get actor my-counter-microvm-1 -a ate-demo-counter-microvm
Verify on GVisor
kubectl ate get actor my-counter-1 -a ate-demo-counter
- When finished, you can manually suspend the actor:
Suspend on MicroVM
kubectl ate suspend actor my-counter-microvm-1 -a ate-demo-counter-microvm
Suspend on GVisor
kubectl ate suspend actor my-counter-1 -a ate-demo-counter
The snapshot stays on the worker’s node and is uploaded to snapshot storage in
the background. Add --mode=sync to wait for the upload instead.
Repeat the curl from step 1 and the actor resumes from its snapshot —
possibly on a different worker — with both counters continuing from where
they left off: the memory count comes back from the MEMORY snapshot’s process
memory, the file counter from the durable volume.
- To permanently delete the suspended actor:
Delete from MicroVM
kubectl ate delete actor my-counter-microvm-1 -a ate-demo-counter-microvm
Delete from GVisor
kubectl ate delete actor my-counter-1 -a ate-demo-counter
Reaching a non-default port
counter.go also listens on a second, configurable port (--extra-port=9090
in this demo’s template) to demonstrate atenet-router’s arbitrary-port ingress
support. Pass [local-port:]remote-port arguments to kubectl ate proxy actor
to forward additional ports. This proxy also takes port 8000, so stop the
earlier one (such as the Quickstart’s) first:
Non standard port with MicroVM
kubectl ate proxy actor my-counter-microvm-1 8000:80 9090:9090 -a ate-demo-counter-microvm
Non standard port with GVisor
kubectl ate proxy actor my-counter-1 8000:80 9090:9090 -a ate-demo-counter
Then send a request to local port 9090:
curl http://localhost:9090/
This reaches the same actor’s second listener and resumes it like any other request.
Current limitation: only HTTP(S) traffic over the tunnel is supported. Raw TCP or other non-HTTP protocols on a non-default port aren’t reachable this way today — if your use case needs that, please open an issue or reach out; the CONNECT tunnel itself is protocol-agnostic, so it’s a matter of prioritizing it, not a fundamental limitation of the design.
Settings
The installer reads these settings in addition to those in docs/guides/operator-install.md, through the same flag, environment variable, and configuration document channels.
| Key | Environment variable | Flag | Default | Applies to |
|---|---|---|---|---|
demo.counter.storageClass | STORAGE_CLASS | --storage-class | standard | deploy demo counter |
demo.counter.withExternalVolume | ATE_DEMO_COUNTER_WITH_EXTERNAL_VOLUME | --with-external-volume | false | deploy demo counter |
How to Uninstall
To remove the counter demo resources from your cluster, run:
Delete from MicroVM
go run ./cmd/ate-setup delete demo counter-microvm
Delete from GVisor
go run ./cmd/ate-setup delete demo counter
This deletes the actors created from the demo templates, the templates themselves (along with their golden actors and golden snapshots, server-side), the demo atespaces, and the worker pools.