go run ./cmd/mermaidlive
↓
- to change the default countdown delay, provide the option, e.g.
-delay 150ms
to only generate UI resources from ui-src, run:
go run ./cmd/mermaidlive -transpile
to build a binary with embedded UI:
go build --tags=embed ./cmd/mermaidlive
- developer experience of live reload back-end →
ResourcesRefreshed
→ front-end - UI served from a binary-embedded filesystem
- pub-sub long-polling connected clients and live viewers
- asynchronously running state machine observable via published events
- live re-rendering of a mermaid chart via events
- initial rendering of a chart via the
LastSeenState
event published upon connecting to the stream - deployment on fly.io
- sharing Gherkin features between unit, API and Browser tests, and sharing step implementations between scenarios
- asynchronously connected client tests: two API and Browser clients
- long poll re-connects and showing the connection status to the user
- a persistent distributed G-Counter (grow-only counter) CRDT for started connections that is eventually-consistent, once service replicas see each other
- service discovery via fly.io internal DNS polling
- replication via a fully-connected ZeroMQ ineternal network mesh
- a simple persistence of the CRDT counter in a continuously re-written JSON-structured file located on machine-bound fly.io volumes
- not using a separately deployed database for the CRDT
flowchart LR
Server --serves---> UI
StateMachine --runs on --> Server
UI --posts commands to --> Server
Server --forwards commands to -->StateMachine
Server --publishes events to -->PubSub
StateMachine --publishes events to -->PubSub
Server --subscribes each connected client to -->PubSub
Server --streams events to --> UI
- the specification contains shared steps
- state machine-level "unit" test steps
- exececise the async state machine
- API-level test steps
- start the server at port
8081
- exercise the specification, including scenarios tagged with
@api
- start the server at port
- Browser-level: test steps
go test -v ./...
- the test starts a temporary server instance and runs the tests against it
./scripts/test-api.sh
- the test uses Playwright via the cucumber-playwright template
- prerequisites:
- install dependencies:
npm i
- initialize Playwright:
npx playwright install
- install dependencies:
- start the server first, e.g.:
./scripts/run-dev.sh -delay 150ms
- run the tests:
./scripts/test-ui.sh
- Mermaid API
- JSON Streaming
- Identifiable concurrent processes are modeled with phony (Go)
- Distributing shared state from the server to client connections via Pub/Sub
- currently deployed on fly.io → mermaidlive.fly.dev
- docker compose
to start 3 initial replicas:
docker compose up -d
→ visit and reload the UI: http://localhost:8080/
stop 2 replicas:
docker stop mermaidlive-mermaidlive-2 mermaidlive-mermaidlive-3
↓
log:
mermaidlive-1 | Peers changed [172.19.0.4 172.19.0.5] -> []
reload the UI a couple of times and re-start the replicas:
docker start mermaidlive-mermaidlive-2 mermaidlive-mermaidlive-3
↓
log:
mermaidlive-1 | Peers changed [] -> [172.19.0.4 172.19.0.5]
mermaidlive-1 | Connecting to tcp://[172.19.0.4]:5000
mermaidlive-1 | Connecting to tcp://[172.19.0.5]:5000
mermaidlive-2 | New visitor count: 25
mermaidlive-3 | New visitor count: 25
mermaidlive-2 | Peers changed [172.19.0.2] -> [172.19.0.2 172.19.0.5]
mermaidlive-2 | Connecting to tcp://[172.19.0.5]:5000
mermaidlive-3 | Peers changed [172.19.0.2] -> [172.19.0.2 172.19.0.4]
mermaidlive-3 | Connecting to tcp://[172.19.0.4]:5000
observe, how the replicated visitor count is propagated to the replicas.
Scale replicas for further experiments, e.g.:
docker compose scale mermaidlive=5
copying the replica file to the local filesystem reveals the structure of the G-Counter:
docker compose cp mermaidlive:/appdata/my.gcounter .
↓
{
"peers": {
"3d226c43af66": 7,
"c2206af9aba2": 9
}
}
which results in the observable counter value of 16
.