Implement v2 Protocol. Adding expectedVersion for optimistic concurrency and metadata.
This commit is contained in:
@ -8,8 +8,12 @@ import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
const EMPTY_STREAM = uint32(0)
|
||||
var CRLF = []byte {'\r', '\n'}
|
||||
|
||||
type DailyDiskStorage struct {
|
||||
storagePath string
|
||||
indexesPath string
|
||||
@ -94,7 +98,7 @@ func readIndexNextEntry(f *os.File) (*IndexEntry, error) {
|
||||
return &index, nil;
|
||||
}
|
||||
|
||||
func writeEvent(filename string, data []byte) error {
|
||||
func writeEvent(filename string, data []byte, metadata []byte) error {
|
||||
eventFile, err := os.OpenFile(filename, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -102,12 +106,26 @@ func writeEvent(filename string, data []byte) error {
|
||||
defer eventFile.Close()
|
||||
|
||||
eventFile.Write(data)
|
||||
eventFile.Write(CRLF)
|
||||
eventFile.Write(metadata)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readEvent(filename string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
func readEvent(filename string) (data []byte, metadata []byte, err error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sep := bytes.Index(content, CRLF)
|
||||
if sep == -1 {
|
||||
data = content
|
||||
metadata = make([]byte, 0)
|
||||
return
|
||||
}
|
||||
data = content[:sep]
|
||||
metadata = content[sep+2:]
|
||||
return
|
||||
}
|
||||
|
||||
func (me DailyDiskStorage) Write(event *StoredEvent) error {
|
||||
@ -115,7 +133,7 @@ func (me DailyDiskStorage) Write(event *StoredEvent) error {
|
||||
eventFilename := me.getEventFilename(event.CreationTime, event.TypeId)
|
||||
os.MkdirAll(path.Dir(eventFilename), 0777)
|
||||
|
||||
err := writeEvent(eventFilename, event.Data)
|
||||
err := writeEvent(eventFilename, event.Data, event.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -135,11 +153,33 @@ func (me DailyDiskStorage) Write(event *StoredEvent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me DailyDiskStorage) StreamVersion(streamId uuid.UUID) (uint32, error) {
|
||||
indexFile, err := os.OpenFile(me.getStreamIndexFilename(streamId), os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return EMPTY_STREAM, errors.New("NOT_FOUND: " + err.Error())
|
||||
}
|
||||
defer indexFile.Close()
|
||||
|
||||
ver := EMPTY_STREAM
|
||||
for {
|
||||
_, err := readIndexNextEntry(indexFile)
|
||||
if err != nil && err.Error() == "EOF" {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return EMPTY_STREAM, err
|
||||
}
|
||||
ver++
|
||||
}
|
||||
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
func (me DailyDiskStorage) ReadStream(streamId uuid.UUID) ([]*StoredEvent, error) {
|
||||
|
||||
indexFile, err := os.OpenFile(me.getStreamIndexFilename(streamId), os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.New("NOT_FOUND: " + err.Error())
|
||||
}
|
||||
defer indexFile.Close()
|
||||
|
||||
@ -152,11 +192,11 @@ func (me DailyDiskStorage) ReadStream(streamId uuid.UUID) ([]*StoredEvent, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := readEvent(me.getEventFilename(indexEntry.creationTime, indexEntry.typeId))
|
||||
data, metadata, err := readEvent(me.getEventFilename(indexEntry.creationTime, indexEntry.typeId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
event := &StoredEvent{streamId, indexEntry.creationTime, indexEntry.typeId, data}
|
||||
event := &StoredEvent{streamId, indexEntry.creationTime, indexEntry.typeId, data, "Metadata", metadata}
|
||||
events = append(events, event)
|
||||
}
|
||||
|
||||
@ -179,11 +219,11 @@ func (me DailyDiskStorage) ReadAll() ([]*StoredEvent, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := readEvent(me.getEventFilename(indexEntry.creationTime, indexEntry.typeId))
|
||||
data, metadata, err := readEvent(me.getEventFilename(indexEntry.creationTime, indexEntry.typeId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
event := &StoredEvent{indexEntry.streamId, indexEntry.creationTime, indexEntry.typeId, data}
|
||||
event := &StoredEvent{indexEntry.streamId, indexEntry.creationTime, indexEntry.typeId, data, "Metadata", metadata}
|
||||
events = append(events, event)
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,10 @@ func TestAddEvent(t *testing.T) {
|
||||
aggregateId := uuid.NewV4()
|
||||
aType := "myType"
|
||||
data := []byte("{}")
|
||||
metadata := []byte("{}")
|
||||
|
||||
//Act
|
||||
err := storage.Write(&StoredEvent{aggregateId, aTime, aType, data})
|
||||
err := storage.Write(&StoredEvent{aggregateId, aTime, aType, data, "Metadata", metadata})
|
||||
|
||||
//Assert
|
||||
if err != nil {
|
||||
@ -54,9 +55,9 @@ func TestReadStream(t *testing.T) {
|
||||
storage := NewDailyDiskStorage(storagePath)
|
||||
|
||||
streamId := uuid.NewV4()
|
||||
ev1 := &StoredEvent{streamId, time.Now(), "1stType", []byte("1stEvent")}
|
||||
ev1 := &StoredEvent{streamId, time.Now(), "1stType", []byte("1stEvent"), "Metadata", []byte("{}")}
|
||||
storage.Write(ev1)
|
||||
ev2 := &StoredEvent{streamId, time.Now(), "2ndType", []byte("2ndEvent")}
|
||||
ev2 := &StoredEvent{streamId, time.Now(), "2ndType", []byte("2ndEvent"), "Metadata", []byte("{}")}
|
||||
storage.Write(ev2)
|
||||
|
||||
//Act
|
||||
@ -89,11 +90,11 @@ func TestReadAll(t *testing.T) {
|
||||
|
||||
stream1Id := uuid.NewV4()
|
||||
stream2Id := uuid.NewV4()
|
||||
ev1 := &StoredEvent{stream1Id, time.Now(), "1stType", []byte("1stEvent")}
|
||||
ev1 := &StoredEvent{stream1Id, time.Now(), "1stType", []byte("1stEvent"), "Metadata", []byte("{}")}
|
||||
storage.Write(ev1)
|
||||
ev2 := &StoredEvent{stream2Id, time.Now(), "2ndType", []byte("2ndEvent")}
|
||||
ev2 := &StoredEvent{stream2Id, time.Now(), "2ndType", []byte("2ndEvent"), "Metadata", []byte("{}")}
|
||||
storage.Write(ev2)
|
||||
ev3 := &StoredEvent{stream1Id, time.Now(), "3rdType", []byte("3rdEvent")}
|
||||
ev3 := &StoredEvent{stream1Id, time.Now(), "3rdType", []byte("3rdEvent"), "Metadata", []byte("{}")}
|
||||
storage.Write(ev3)
|
||||
|
||||
//Act
|
||||
|
@ -111,6 +111,11 @@ func (me SimpleDiskStorage) Write(event *StoredEvent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me SimpleDiskStorage) StreamVersion(streamId uuid.UUID) (uint32, error) {
|
||||
//TODO
|
||||
return EMPTY_STREAM, nil
|
||||
}
|
||||
|
||||
func (me SimpleDiskStorage) ReadStream(streamId uuid.UUID) ([]*StoredEvent, error) {
|
||||
streamName := streamId.String()
|
||||
offset := int64(0) //TODO snapshots
|
||||
@ -134,7 +139,8 @@ func (me SimpleDiskStorage) ReadStream(streamId uuid.UUID) ([]*StoredEvent, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
event := &StoredEvent{streamId, creationTime, typeId, data}
|
||||
//TODO metadata
|
||||
event := &StoredEvent{streamId, creationTime, typeId, data, "", nil}
|
||||
results = append(results, event)
|
||||
}
|
||||
return results, nil
|
||||
@ -197,7 +203,8 @@ func (me SimpleDiskStorage) retrieveStoredEvent(streamId uuid.UUID, offset int64
|
||||
return nil, err
|
||||
}
|
||||
|
||||
event := &StoredEvent{streamId, creationTime, typeId, data}
|
||||
//TODO metadata
|
||||
event := &StoredEvent{streamId, creationTime, typeId, data, "", nil}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ type StoredEvent struct {
|
||||
CreationTime time.Time
|
||||
TypeId string
|
||||
Data []byte
|
||||
MetadataTypeId string
|
||||
Metadata []byte
|
||||
}
|
||||
|
||||
//TODO: performance - change reads array for some kind of iterator
|
||||
@ -20,4 +22,5 @@ type Storage interface {
|
||||
Write(event *StoredEvent) error
|
||||
ReadStream(streamId uuid.UUID) ([]*StoredEvent, error)
|
||||
ReadAll() ([]*StoredEvent, error)
|
||||
StreamVersion(streamId uuid.UUID) (uint32, error)
|
||||
}
|
||||
|
Reference in New Issue
Block a user