CanardAbsurd

CanardAbsurd hex sticker: Ce n’est pas un canard.

Lifecycle: experimental R-CMD-check Documentation R-universe

CanardAbsurd runs expensive R work that must outlive the request that started it. Submit a task, let any worker pick it up, and if that worker dies, the next one resumes from the last saved step instead of starting over.

It brings the checkpoint-and-replay model of Absurd to R, with DuckDB holding the state and Quack giving other processes access to it.

The package is experimental and has no users outside itself. The API, the SQL schema and the stored format all change without deprecation cycles or migrations, in favour of a clearer model.

Install

install.packages("CanardAbsurd", repos = "https://rgenomicsetl.r-universe.dev")

Example

library(CanardAbsurd)
db <- ca_open()

total <- function(input, task) {
  subtotal <- ca_step(task, "subtotal", function() sum(input$amounts))
  ca_step(task, "total", function() subtotal * (1 + input$tax))
}

id <- ca_spawn(db, "total", list(amounts = c(20, 10), tax = 0.2), id = "invoice-42")
ca_work(db, list(total = total), max_tasks = 1)
ca_result(db, id)
#> [1] 36
ca_close(db)

A task is one durable request. Its handler is the R function that runs it, and each ca_step() is a named checkpoint. If a worker dies after subtotal is saved, the next attempt replays subtotal and computes only total.

How it fits together

flowchart LR
  Producer["API or producer"] -->|"ca_spawn()"| Server
  Worker1["R worker"] -->|"ca_work()"| Server
  Worker2["R worker"] -->|"ca_work()"| Server
  subgraph Owner["database owner"]
    Server["Quack server<br/>ca_serve()"] --> DB[("DuckDB file")]
  end

Guarantees

Inputs, results and checkpoints are native DuckDB values you can query in SQL: vectors, lists, data frames, dates, timestamps, factors, raw vectors and integer64. Serialize anything else to a raw vector yourself.

Guides