Created
July 11, 2024 14:37
-
-
Save maxsei/ca14d6f91e944a437f0300f12246cd01 to your computer and use it in GitHub Desktop.
crazy event store π
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eventstore | |
import ( | |
"errors" | |
"sync/atomic" | |
"time" | |
) | |
var ( | |
ErrCas = errors.New("CAS error") | |
ErrOldEvent = errors.New("event could not replace newer event") | |
) | |
const ( | |
PUT Command = iota | |
DELETE | |
PURGE | |
) | |
type Command int | |
type Event[K comparable] interface { | |
Key() K | |
At() time.Time | |
} | |
type EventCommand[K comparable, V Event[K]] struct { | |
Event V | |
Command Command | |
} | |
type EventCommands[K comparable, V Event[K]] List[EventCommand[K, V]] | |
type EventStore[K comparable, V Event[K]] struct { | |
events atomic.Value | |
} | |
func (s *EventStore[K, V]) swapRoot(curr, prev EventCommands[K, V]) (EventCommands[K, V], error) { | |
if !s.events.CompareAndSwap(prev, curr) { | |
return nil, ErrCas | |
} | |
return curr, nil | |
} | |
func (s *EventStore[K, V]) do(cmd EventCommand[K, V]) (EventCommands[K, V], error) { | |
prev := s.events.Load().(EventCommands[K, V]) | |
if prev.Len() == 0 { | |
return s.swapRoot(prev, prev.Conj(cmd)) | |
} | |
var left []EventCommand[K, V] | |
var right EventCommands[K, V] | |
for p := prev; p.Len() > 0; p = p.Rest() { | |
if p.First().Event.Key() != cmd.Event.Key() { | |
left = append(left, *p.First()) | |
continue | |
} | |
// Command happend after registed command so skip. | |
if p.First().Event.At().Before(cmd.Event.At()) { | |
return nil, ErrOldEvent | |
} | |
right = p.Rest() | |
if right == nil { | |
right = NewList[EventCommand[K, V]]() | |
} | |
break | |
} | |
for i := range left { | |
right = right.Conj(left[len(left)-1-i]) | |
} | |
right = right.Conj(cmd) | |
return s.swapRoot(prev, right) | |
} | |
func (s *EventStore[K, V]) Put(e V) (EventCommands[K, V], error) { | |
return s.do(EventCommand[K, V]{e, PUT}) | |
} | |
func (s *EventStore[K, V]) Del(e K) (EventCommands[K, V], error) { | |
return s.do(EventCommand[K, V]{DELETE}) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eventstore | |
type List[T any] interface { | |
Len() int | |
// Conj returns a new list with an the new value as the first value. | |
Conj(T) List[T] | |
First() *T | |
Rest() List[T] | |
} | |
func NewList[T any]() List[T] { | |
return new(list[T]) | |
} | |
type list[T any] struct { | |
first T | |
rest *list[T] | |
count int | |
} | |
func (l *list[T]) Len() int { | |
return l.count | |
} | |
func (l *list[T]) Conj(val T) List[T] { | |
return &list[T]{val, l, l.count + 1} | |
} | |
func (l *list[T]) First() *T { | |
if l.count == 0 { | |
return nil | |
} | |
return &l.first | |
} | |
func (l *list[T]) Rest() List[T] { | |
return l.rest | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment