Skip to content

Instantly share code, notes, and snippets.

@maxsei
Created July 11, 2024 14:37
Show Gist options
  • Save maxsei/ca14d6f91e944a437f0300f12246cd01 to your computer and use it in GitHub Desktop.
Save maxsei/ca14d6f91e944a437f0300f12246cd01 to your computer and use it in GitHub Desktop.
crazy event store πŸ™ƒ
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})
}
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