slct
Demo API is available at http://51.158.121.7:8081/
Feel free to open issues in case you'll find a bug or have a nice idea :)
CSP
This service implements Go's CSP model over HTTP in persistent mode. It allows you to use select statement across multiple machines with same behavior and guarantees.
The main object of the API is state, which holds user-defined dump of a process state and a list of currently blocked selects.
Each state can have multiple threads/coroutines/goroutines - for example if you have process that have 2 blocked goroutines - you will end up with 2 selects in state
The process definition & implementation is completely up to the user and not limited to Go or any other language. You can write business logic in JS without issues. Although writing in Go will allow you to leverage automatic state-machine generation
When all selects are blocking - the status of the state waiting - i.e. nothing happens. But when at least 1 select is resolved and unblocks - result is added to this select, and state status will be pending and your service is going to continue execution of this process/state.
There are 2 ways to resume process execution:
- If callback is set - it will automatically triggered whenever process needs to be resumed. Callback will update state and return it in HTTP response back to service to save it.
- If callback is not set - you have to poll the pending states, update them and manually save them with PUT /state/{id} request.
Caveats:
- You have to process and put back the message within X seconds. Like with a normal message queue.
- When state is processed - all selects that had result must be deleted. Selects that did not had result must not be deleted.
API
To start the process - we create state with initial result that contains the process initial select(s).
PUT /state/1
{
"Data": "... initial process state...",
"Selects": [{...}],
"Callback": {
Type: 1,
URI: "http://.../callback"
}
}
- If you wish to start the process immediately - you can use default select statement to achieve that.
- You can omit Callback if you are planning to poll this service
- You can just omit the Data if you want to execute only select statement.
- State creation operation can succeed only once, if the state was already created - you will receive lock error.
Callback
To support callback variant of resuming the process - add Callback to your state.
When state will be ready to be resumed - the callback will be called with the state and expect updated state returned back:
{
"Selects": [{
ID: "1",
"Cases": [{
"Op": 1,
"Chan": "chan1 id"
},{
"Op": 3,
"Chan": "15600421234"
},{
"Op": 2,
"Chan": "chan2 id"
"Data": "hello world"
}]
}],
"Data": "... updated process state ...",
"Results": [{...}],
"Callback": {
Type: 1,
URI: "http://.../callback"
},
}
Polling
To support polling resume variant - omit callback from state, poll pending states periodically, process them and put them back using PUT /state/{id} within X seconds
Polling request looks like this: GET /state/poll?prefix=state_id_prefix&wait=30
prefix is a simple filter to poll only a subset of all pending. wait parameter can be used to do long-polling and block the state until some results are present.
Poll response:
[{
"Selects": [{
ID: "1",
"Cases": [{
"Op": 1,
"Chan": "chan1 id"
},{
"Op": 3,
"Chan": "15600421234"
},{
"Op": 2,
"Chan": "chan2 id"
"Data": "hello world"
}]
}],
"Data": "... updated process state ...",
"Results": [{...}],
"Callback": {
Type: 1,
URI: "http://.../callback"
},
}]
Installation
The service uses Raft protocol to linearize all select operations. This allows us to build exactly the same CSP model that is present in Go, but in highly-available manner.
This means you have to run 3 select nodes with > 2GB RAM to make it work reliably 24/7.
This also means that all of the nodes should have NTP setup correctly, as the system time for time channels is taken from the Raft leader, which can be any node.
Limits:
- On a typical SSD server 3 node cluster with 10KB payloads you'll get ~1000-5000 req/sec, which should be way enough for asyncronous tasks management.
- Number of active(waiting) selects in your cluster is limited by SSD size
- 1MB max state size (soft limit). The system is limited to
50100MB/s thoughput, so bigger states decrease req/sec and increases RAM usage.
Warning
This is not production ready. Use at your own risk.
TODO
- Get feedback
- Integrate with async lib
- Lock + Results easier concurrency handling