In this page
World Basics
A world is a named branch of the store. Each world has its own block index: the tasks running in a world read and commit to that index instead of main’s. The world and its base share the blocks they have in common, so only what a world writes takes extra space. The main store is world 0, and its name, main, is reserved.
Creating and removing worlds
All functions are static functions of runtime::World:
World::create("what-if", WorldMode::snapshot, null); // returns the world id; base null: main
World::create("variant", WorldMode::overlay, "what-if"); // based on "what-if" rather than main
World::list(); // Array<WorldInfo>: the worlds the caller may use
World::id("what-if"); // int?, null when there is none
World::current(); // the world the calling task runs in, "main" for main
World::reset("variant"); // empties it of what its tasks wrote
World::remove("variant"); // removes it and its block index
- Names take 1 to 128 characters among
[A-Za-z0-9_.-]. Ids are never reused, even after a world is removed. create,resetandremoveneed theworldpermission. Theadminrole holds it, the built-inuserrole does not (see Namespaces).- A world can be reset or removed only by its creator or by an admin, and not while a task still runs in it or while another world is based on it.
WorldInfodescribes a world:id,name,mode,created,user_id(its creator) andbase.create,resetandremoveeach log aninforecord whosedatais the world’sWorldInfo, so the log keeps the history of the worlds.
World::reset is the cheap way to reuse a world. It empties the world of what its tasks wrote and starts it again from its base as the base is now. The world keeps its id, its name, its creator and the users allowed to use it, which remove followed by create would not keep.
The three modes
| Mode | Starts with | Sees the later commits of its base | Cost of creation |
|---|---|---|---|
overlay |
an empty index that reads its base | Yes, for every node the world has not modified. A node the world modified stays as the world left it. | Instant |
snapshot |
a copy of its base’s block index | Never: the world is its base as it was at creation | About 50 ms per million blocks, during which commits wait |
standalone |
an empty index, no base | Nothing to see: like a new store, its module variables start as empty nodes of its own | Instant |
Overlay is the mode for simulation and search. It is created at once and stores only what it changes. It also keeps following its base: while a scenario world is open, the live measurements written to main keep reaching the nodes the scenario did not touch.
Snapshot freezes the base. Use it when a computation must see the graph exactly as it was at one moment, whatever main receives afterwards.
Standalone is an empty graph in the same server: the mode for a namespace that shares nothing with the others.
The difference between overlay and snapshot shows on live data. Fork both from main, then write a new measurement in main:
// in main
World::create("ov", WorldMode::overlay, null);
World::create("snap", WorldMode::snapshot, null);
v.speed.setAt(start + 20hour, 11.0); // a new measurement, in main
// in "ov": v.speed.getAt(v.speed.lastTime()!!) is 11.0, the new point
// in "snap": v.speed.getAt(v.speed.lastTime()!!) is still main's last point when "snap" was created
Chains of worlds
A world can be based on another world rather than on main: a scenario of a scenario. An overlay world based on an overlay world reads, for what it does not hold, what its base reads, down to the first world that is not an overlay. A chain holds at most 8 overlay worlds, and each level adds one index lookup to a read the world does not hold.
A snapshot based on an overlay world starts from what that overlay reads when the snapshot is created, then follows none of the chain.
The unit of isolation is the node
In an overlay world, what is copied on write is the node: a node<T>, an object, or a whole nodeTime, nodeIndex, nodeList or nodeGeo. The first time an overlay world writes to a large nodeIndex its base holds, it copies the index entries of the node’s tree (the entries, not the blocks). After that first write, two tasks of the same world writing the same node merge as they do in main.
Keep this in mind when you model: a nodeTime that a scenario writes to is the scenario’s from then on. The scenario sees the base’s later points for that series only if it never wrote to it.
Running code in a world
A task runs in exactly one world. You pick it when you start the task:
| From | How |
|---|---|
| An HTTP call | The request header world: <name>. Without the header, the call runs in main. An unknown name answers 400, a world the caller may not use 403. |
spawn |
Job { function: f, arguments: [...], world: "<name>" }. null runs in the spawning task’s world. |
await |
Always the awaiting task’s world. A job naming another world is an error. |
| The scheduler | PeriodicOptions { world: "<name>" }. A run is skipped while its world does not exist. |
| The CLI | greycat run --world=<name> <fn> and greycat call --world=<name> <fn>, or GREYCAT_WORLD. |
curl -H "world: scenario-a" -X POST -d '[]' http://localhost:8080/project::kpis
// evaluate a plan in its own world, in the background
var id = spawn(Job { function: evaluate, arguments: [plan], world: "scenario-a" });
// re-run a forecast every 15 minutes, in a world
Scheduler::add(forecast, FixedPeriodicity { every: 15min }, PeriodicOptions { world: "forecast" });
The same code runs in main and in every world: a function does not know, and does not need to know, which world it runs in. World::current() tells it when it matters.
A task cannot read another world than its own. To bring a result from a world back to main, the task in the world returns it (see Scenario Search), or a client calls an exposed function in the world and receives the result.
Limits
- No merge. A world cannot be merged back into main. What a scenario must leave behind is written to main explicitly, as data, or the world is kept and shared.
- One world per task. A task, and the
awaitjobs it runs, see one world. UsespawnwithJob.worldto work in several. - Backups include the worlds.
Runtime::backup_fullandgreycat backupsave every world, storing the blocks a world shares with main once. A backup must run in main.greycat restorereplaces the worlds of the store with those of the archive. - Storage stats.
Runtime::store_stats()lists each world with the blocks it holds that main does not, and their size.