Rewind in Go
This is the next generation of Rewind. The intention is to bring higher concurrency and support for multiple streams. Initially only a single storage backend will be supported (LevelDB).
The implementation is written in the Go programming language.
Currently, this project is highly experimental and alpha.
- Tests are executed using make test.
- I'll try to stick to http://www.semver.org when it comes to versioning.
Have you ever been nervous of all those DBMSs schema changes when you are deploying your applications? They are gonna take too long, or break backward compatibility? Have you ever thought "Crap, I wish I had stored that information since earlier"? Have you ever felt your writing patterns and your reading patterns differ a lot, making things harder to scale? Issues like these can be solved using CQRS and event sourcing.
CQRS (Command-Query Response Segregation) is an architectural pattern that aims to solve these issues by splitting up your architectural system into two parts:
- A write side that takes care of validating input and optimizes for fast writes. The write side takes commands and outputs corresponding events if the command validates correctly.
- A read side that listens to incoming events from the write side. The read side is optimized for fast reads and incrementally build up state that can be queried fast.
While not required, it is common to use messaging between the write and read sides. This means that the system will be in an inconsistent state from time to time. This is usually not an issue and can be overcome in various ways.
A common pattern used together with CQRS is event sourcing. The concept can be summarized as using state changes as primary persistence, instead of the final state. The state changes are called events and they are generated by the write side and delivered to the read side.
The events are persisted in an event store that sits inbetween the read and write side of things. It takes care of three things:
- persisting all events to disk.
- being a hub/broker replicating all events from the write to the read side of things.
- allowind fast querying of events so that different parts of the system can be synced back on track and new components can be brought back in play.
Gorewind is an event store application that talks ZeroMQ and is written in Go. It is also a library that can be used to embed event store functionality into a Go application.
There are currently no releases of Gorewind. However, if you would like to build Gorewind from code you need to do the following:
- Install Go 1.0 or 1.1.
- Make sure libzmq/libzmq3 (ZeroMQ) version 3 is installed on the system together with its development files. It has been tested to work with version 3.2.
- Ser up a GOPATH environment variable. You can read more about it here: http://golang.org/doc/code.html
$ git clone https://github.com/JensRantil/gorewind.git $ go get -tags zmq_3_x github.com/alecthomas/gozmq $ go get ... $ make test ... (make sure tests are not failing) $ make build
...and voilá, gorewind binary should have been created.
Gorewind stores ordered events in independent event streams (usually only referred to as streams in code and documentation). Each event has a unique identifier (within its stream) and some data.
The identifier for an event is used by clients to be able to query events. The identifier is a variable length byte array and should be treated as such by the client.
The event data is treated as a variable length series of bytes by Gorewind server. Therefor, the server has no concept of whether the event is JSON, XML, protobuf, whatever. Gorewind does also not store any meta data about each event, such as creation date, origin, author etc. Both serialization format and meta data should be dealt with by the event store clients.
Talking to gorewind
The Gorewind server has two different wire protocols. Each is using ZeroMQ as low level transport protocol. Each wire protocol has one single ZeroMQ endpoint in Gorewind:
- A streaming response (ROUTER) socket which is used for receiving new events and querying chronological slices of all events throughout time. The connecting ZeroMQ socket must be of type DEALER. The reason for not using a plain REQ/REP constellation is because they would not handle streaming of bigger results. See the "Advanced Request-Reply Patterns" in the ZeroMQ Guide for more information.
- A streaming (PUB) socket which publishes submitted events that has been persisted. The published events can be filtered based on the stream.is used by all clients that are interested in all new incoming events.
Each endpoint is configurable through command line when starting
gorewind --help to get a list of the specific
command line arguments
gorewind can handle.
Note that the wire protocol is still under heavy development. Pull requests and improvement proposals are welcome!
The socket for querying Gorewind is the one which has the most advanced wire protocol. The socket is of type ROUTER, which makes is possible for Gorewind to handle incoming requests concurrently. Since the client socket is a DEALER, the server can return arbitrarily many reply messages for each request. A typical converation between a client (C) and Gorewind (R) looks like this:
C: Request R: Response ... (1 to N responses) R: Response C: Request R: Response ...
Each request is a multipart message. The first part is a string that specifies the type of request. There are multiple request types:
Used for publishing an event. Apart from the command header, it consists of two frames:
- Stream identifier used to specify which stream the event should be added to. Treated simply as a byte array, but it's recommended to keep it an ASCII string to for facilitate easier debugging.
- Event data that describes the event that happened. Gorewind does not know anything about the serialization format. It always simply stores the bytes. However, it is recommended to keep the format simple (such as JSON) to facilitate debugging.
Each new incoming/published event triggers that it is to be streamed out to all listening clients.
On successful reception of an event, Gorewind responds with a 2-framed message where:
- the first message frame contains the ASCII bytes
- the second frame contains the event id for the newly published message.
See "Error response" below for how errors are dealt with.
Used for querying for older events. For the
QUERY request type the
next three message parts must be:
- stream that is to be queried.
- an optional event id, or an empty part. Restricts the earliest (chronologically) incoming message that we are interested in to all messages received after the event with the specified event id. Note that this does not include the message with the specified event id. If this part of the message is empty, no lower restriction is made and messages will be returned starting from the first event ever seen.
- an optional event id, or an empty part. Restricts the latest (chronologically) incoming message that we are interested in to all messages received before, or including, the event with the specified event id. If this part of the message is empty, no upper restriction is made and messages will be returned starting from the first event ever seen.
If you are a data structure type-of-guy you could view Gorewind as an application that stores a bunch of named insert-ordered maps (event id => event) that allows querying of ranges of events based on event ids.
There are two types of responses that can be given upon a query:
An error. See "Error response" below; or
Multiple messages, one for each event that matches the query, followed by a single stop message that signals that no further messages will be returned. The events are returned in the same order they were published in.
- Each event message is a multipart message consisting of three frames:
- The ASCII content "EVENT".
- The event id for the event in question.
- The event data for the event in question.
- The stop message is a single framed message consisting of the
END. After the stop message has been sent, no further messages will be sent from the server.
If anything goes wrong, a single framed message starting with the ASCII
ERROR, followed by a space (32), will be sent with the
response. This means an error occured. The rest of message contains a
human readable (ASCII) description of the actual error that occured.
This information can be highly useful for remote clients to debug any
problems that might arise.
After an error message has been sent, no further messages will be sent from Gorewind.
Event stream (PUB socket)
Every incoming event gets broadcast to all sockets connected to the streaming socket. The streaming socket a ZeroMQ socket of type PUB.
Every message received automatically gets assigned a unique (within its stream) event id . This event id is used for querying events (see below). Each sent message from the streaming is a multipart message that consists of two parts:
- The event stream that the event belongs to.
- The event's unique identifier within its event stream. The client should view this as a series of bytes.
- The event content. This is the exact same bytes that were sent to the server when the event was to be published.
Getting started developing rewind is quite straightforward. The library uses setuptools and standard Python project layout for tests etcetera.
Spelling mistakes, bad grammar, wire format improvements, test improvements and other feature additions are all welcome. Please issue pull requests or create an issue if you'd like to discuss it on Github.
Why the name "Gorewind"?
"Gorewind" is a rewrite of "Rewind". The name "Rewind" was chosen because:
- Rewind can look at what happened in the past and replay the events since then.
- It's time to rewind and rethink the way we are storing state. Disk is cheap.
This package has been developed by Jens Rantil <email@example.com>. You can also reach me through snailmail at:
Jens Rantil Brållunden 4 16774 Bromma SWEDEN