voyager-vm
is a queue-based message processing system and high-level virtual machine. It functions similarly to RabbitMQ or Kafka, while also providing additional components to build an efficient and reliable message-based program.
flowchart
queue(queue) --> |pop front| process{process}
enqueue{enqueue} -->|push back| queue
process --> success
process --> error
error --->|retryable| enqueue
error -->|fatal| drop{drop}
success -->|returned new messages| filter{filter}
filter -->|interest| topic_queue(topic queue)
filter -->|no interest| enqueue
topic_queue -->|pop all messages by topic| run_pass{run pass}
run_pass --> optimize_further[optimize further] & ready
optimize_further --> topic_queue
ready --> filter
There are two queues. The main queue, which is a priority queue, is where messages are pulled from to be processed. Older messages are processed first, with new messages being pushed to the back. There is also the topic queue, where messages are tagged with a topic, and then a pass is run over all messages with the same topic. Messages are pushed to the topic queue based on an interest filter, which is run on every message before it is pushed to the main queue.
Every message is defined as an opcode, which defines additional semantics. The main opcodes are:
Call
: Execute an action. This is the main opcode that will be used for most functionality.Data
: An inert piece of data. Outside ofPromise
s, if this is not picked up by an interest filter, it will simply be dropped.Promise
: Contains an internal queue. As items in that queue are processed, any returnedData
messages will be pushed to it's internal data queue. Once all messages have been resolved, the callback is executed with the resolvedData
messages.Seq
: Contains a list of messages, which will be executed in order. The results of processing the first item in the list will be pushed back to the front.Conc
: Contains a list of messages, which will be executed in a round-robin fashion. The results of processing the first item in the list will be pushed to the back of the list.Defer
: Wait until the contained timestamp.
This enables building complex programs. For example, the program seq([call(A), conc([B, C])])
defines messages A
, B
, and C
, where B
and C
must occur after A
.