The Durable Promises implemented in this example work like regular futures/promises, but are durable cross processes and failures.
Can be used to build simple and reliable callbacks, signal and communicate between systems, or to decouple sender/receiver.
- A promise is uniquely identified by an id
- An arbitrary number of awaiters (listeners) across different processes can await the promise.
- That promise can be resolved/rejected once. If multiple attempts to resolve or reject are made, only the first will take effect. The resolution is idempotent.
The promises are a simple but expressive way to signal across distributed processes:
- Their idempotency on resolution guarantees a stable value
- Listeners can await the value for a long time, and retrieve the value again after a failure/restart (await again, get the same value).
- The result is durable once set. Completer and listeners do not need to be alive at the same time.
- It does not matter whether listener or completer comes first.
Using promises from TypeScript
const promiseId = "my-durable-promise-id";
const restateUri = "restate:8080";
// get a reference to a durable promise
const durablePromise = dp.durablePromise<string>(restateUri, promiseId);
// check the promise without blocking
const peeked = await durablePromise.peek();
// awaiting the result
const resultProm = await durablePromise.get();
// completing the promise. if we are the first to complete, the actual result
// will be our completion value
const actualResult = await durablePromise.resolve("This promise will notify everyone");
// Likewise for rejections
const actualResult2 = await durablePromise.reject("Oh dear, rejected");
Using promises via HTTP/curl
-
peek:
curl localhost:8080/durablePromiseServer/peek -H 'content-type: application/json' -d '{ "request": { "promiseName": "prom-1" } }'
-
await:
curl localhost:8080/durablePromiseServer/await -H 'content-type: application/json' -d '{ "request": { "promiseName": "prom-1" } }'
-
resolve:
curl localhost:8080/durablePromiseServer/resolve -H 'content-type: application/json' -d '{ "request": { "promiseName": "prom-1", "value": { "name": "Barack", "email": "[email protected]" } } }'
-
reject:
curl localhost:8080/durablePromiseServer/reject -H 'content-type: application/json' -d '{ "request": { "promiseName": "prom-1", "errorMessage": "help!" } }'
The Durable Promises are a simple application implemented on top of Restate, making use of Restate's Virtual Objects. You can use this simple implementation and add it to your application or infra as a self-contained piece.
- Install the dependencies:
npm install
- Start Restate in one shell:
npx restate-server
(or run via Docker or native binary) - Start the Durable Promises implementation in another shell:
npm run promises
- Register Durable Promises service:
npx restate -y dep reg "localhost:9080" --force
Note: the '--force' flag here is to circumvent all checks relating to graceful upgrades, because this here is only an example/playground, not a production setup.
You can now await and resolve promises from different processes at different times. With via simple HTTP calls (see above) or the TypeScript API.
You can start the bundled examples via npm run example1
, npm run example2
, npm run example3
,
optionally passing [promise-id] [restateUri]
as parameters.