genroc

docs / Guides / Getting started

Surviving a restart

Demonstrating why to use genroc in the first place.

Create process from scratch

Let’s now create a process which will run for a longer time:

# definitions/ticker.genroc.yaml
nameUnique process identifier.: tickerUnique process identifier.
tasksOrdered list of execution tasks. Control advances linearly unless a switch case redirects.:
  - idTask identifier, unique within the definition.: tickTask identifier, unique within the definition.
    actionDescribes the action to perform. Omit for switch-only (routing) tasks.:
      typeDelay action: parks the instance until a duration elapses (for) or an instant arrives (until).: delayDelay action: parks the instance until a duration elapses (for) or an instant arrives (until).
      forA duration from when the task is reached: "2h30m", a number of milliseconds, or a $: expression.: 10sA duration from when the task is reached: "2h30m", a number of milliseconds, or a $: expression.
    outputtasks.tick.output — object{count}:
      countcount — integer: "$: (selfself → object{previous?}.previousself.previous → object{count}|null.countself.previous.count → integer|null ?? 0) + 1(self.previous.count ?? 0) + 1 → integer"
    switchRequired. Routing: a shorthand ("next", "end", "$task-id") or an ordered list of cases.:
      - caseself.output.count < 3 → boolean: "selfself → object{output, previous?}.outputself.output → object{count}.countself.output.count → integer < 3self.output.count < 3 → boolean"
        goto"end" to terminate, "next" to advance, or "$task-id" to jump to a task.: $tick"end" to terminate, "next" to advance, or "$task-id" to jump to a task.
      - goto"end" to terminate, "next" to advance, or "$task-id" to jump to a task.: end"end" to terminate, "next" to advance, or "$task-id" to jump to a task.

outputoutput — object{ticks}:
  ticksticks — integer: "$: outputsoutputs → object{tick}.tickoutputs.tick → object{count}.countoutputs.tick.count → integer ?? 0outputs.tick.count ?? 0 → integer"

Copy this yaml and put it into definitions/ticker.genroc.yaml (we are still working with project produced by genctl init from previous sections).

Let’s apply

Let’s now apply the process:

> genctl apply
latest: hello v1 (current)
latest: ticker - -> v1 (new)

You should see the same output as above. The hello process is unchanged — (current) means nothing moved — and ticker is created.

Run the process

This process doesn’t require any input, so it’s simple to run it.

genctl run ticker

Now let’s check the logs.

genctl logs @last

This process runs for about 30s, so you can run the logs command repeatedly until it ends.

Once it finishes, you should see something like this:

> genctl logs @last
TIME      LEVEL  ID        EVENT             TASK
--- 2026-09-08 +02:00 ---
14:39:51  INFO   4ptydpvz  inst_created                      by=no-auth:anonymous
14:39:51  INFO   4ptydpvz  delay_armed       tick            msg="10s -> 2026-09-08T12:40:01Z"
14:40:01  INFO   4ptydpvz  task_completed    tick            msg="→ $tick"
14:40:01  INFO   4ptydpvz  delay_armed       tick            msg="10s -> 2026-09-08T12:40:11Z"
14:40:12  INFO   4ptydpvz  task_completed    tick            msg="→ $tick"
14:40:12  INFO   4ptydpvz  delay_armed       tick            msg="10s -> 2026-09-08T12:40:22Z"
14:40:22  INFO   4ptydpvz  inst_completed    tick            output={"ticks":3}

Let’s try to break it!

Let’s now start a new instance and immediately stop the docker containers.

genctl run ticker && docker compose stop

Leave the server down for some time and then start it again.

docker compose up -d

Now let’s check the logs:

> genctl logs @last                       
TIME      LEVEL  ID        EVENT             TASK
--- 2026-09-08 +02:00 ---
14:50:53  INFO   20he91zm  inst_created                      by=no-auth:anonymous
14:50:53  INFO   20he91zm  delay_armed       tick            msg="10s -> 2026-09-08T12:51:03Z"
14:52:23  INFO   20he91zm  task_completed    tick            msg="→ $tick"
14:52:23  INFO   20he91zm  delay_armed       tick            msg="10s -> 2026-09-08T12:52:33Z"
14:52:33  INFO   20he91zm  task_completed    tick            msg="→ $tick"
14:52:33  INFO   20he91zm  delay_armed       tick            msg="10s -> 2026-09-08T12:52:43Z"
14:52:44  INFO   20he91zm  inst_completed    tick            output={"ticks":3}

You can see that process started at 14:50:53, but then there is nothing for 90 seconds. Then it recovers and continues.

The instance lives in the database not in the running server. So if the server restarts, nothing is lost.

How about hard crashes?

You might object that docker stops the server gracefully and so it never aborts the running process forcibly, but what if our computer crashes or there is an electricity outage?

Genroc is safe in these cases too - and here the nuance starts. None of the actions in ticker have side-effects, so genroc can simply re-execute the task and continue.