Stateful Counter

Preserve in-memory and durable volume state across actor suspend and resume cycles.

⏱️ 5 mins · 6 min read ·

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=true exported 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).

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 GVisorUse `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: the ate-demo-counter namespace and the counter WorkerPool, then wait for the worker rollout.
  • Create the ate-demo-counter atespace.
  • Create the counter actor template through the ate API (kubectl ate create actor-template) from counter-template.yaml.tmpl. The manifest is the message’s protojson form — the same shape kubectl ate get actor-template -o yaml prints inside its actorTemplates list.
  • 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.

  1. In a separate terminal, send an HTTP POST request to increment the counter:
curl -X POST http://localhost:8000
  1. Verify that the actor is now in a RUNNING state 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
  1. 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.

  1. 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.

KeyEnvironment variableFlagDefaultApplies to
demo.counter.storageClassSTORAGE_CLASS--storage-classstandarddeploy demo counter
demo.counter.withExternalVolumeATE_DEMO_COUNTER_WITH_EXTERNAL_VOLUME--with-external-volumefalsedeploy 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.

Was this page helpful?