chore: make connection paging more like standard
This commit is contained in:
@@ -4,6 +4,7 @@ package diskstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -208,7 +209,7 @@ func (e *eventLog) Append(ctx context.Context, events event.Events, version uint
|
||||
|
||||
return count, err
|
||||
}
|
||||
func (e *eventLog) Read(ctx context.Context, pos, count int64) (event.Events, error) {
|
||||
func (e *eventLog) Read(ctx context.Context, after, count int64) (event.Events, error) {
|
||||
_, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
@@ -233,7 +234,7 @@ func (e *eventLog) Read(ctx context.Context, pos, count int64) (event.Events, er
|
||||
return nil
|
||||
}
|
||||
|
||||
start, count := math.PagerBox(first, last, pos, count)
|
||||
start, count := math.PagerBox(first, last, after, count)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -246,6 +247,10 @@ func (e *eventLog) Read(ctx context.Context, pos, count int64) (event.Events, er
|
||||
var b []byte
|
||||
b, err = stream.Read(start)
|
||||
if err != nil {
|
||||
if errors.Is(err, wal.ErrNotFound) || errors.Is(err, wal.ErrOutOfRange) {
|
||||
err = fmt.Errorf("%w: empty", es.ErrNotFound)
|
||||
}
|
||||
|
||||
span.RecordError(err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type Driver interface {
|
||||
}
|
||||
|
||||
type EventLog interface {
|
||||
Read(ctx context.Context, pos, count int64) (event.Events, error)
|
||||
Read(ctx context.Context, after, count int64) (event.Events, error)
|
||||
Append(ctx context.Context, events event.Events, version uint64) (uint64, error)
|
||||
FirstIndex(context.Context) (uint64, error)
|
||||
LastIndex(context.Context) (uint64, error)
|
||||
|
||||
@@ -112,7 +112,7 @@ func (m *eventLog) Append(ctx context.Context, events event.Events, version uint
|
||||
}
|
||||
|
||||
// Read implements driver.EventStore
|
||||
func (m *eventLog) Read(ctx context.Context, pos int64, count int64) (event.Events, error) {
|
||||
func (m *eventLog) Read(ctx context.Context, after int64, count int64) (event.Events, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
@@ -131,11 +131,11 @@ func (m *eventLog) Read(ctx context.Context, pos int64, count int64) (event.Even
|
||||
return nil
|
||||
}
|
||||
|
||||
start, count := math.PagerBox(first, last, pos, count)
|
||||
start, count := math.PagerBox(first, last, after, count)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
span.AddEvent(fmt.Sprint("box", first, last, pos, count))
|
||||
span.AddEvent(fmt.Sprint("box", first, last, after, count))
|
||||
events = make([]event.Event, math.Abs(count))
|
||||
for i := range events {
|
||||
span.AddEvent(fmt.Sprintf("read event %d of %d", i, math.Abs(count)))
|
||||
|
||||
@@ -47,11 +47,11 @@ type wrapper struct {
|
||||
|
||||
var _ driver.EventLog = (*wrapper)(nil)
|
||||
|
||||
func (w *wrapper) Read(ctx context.Context, pos int64, count int64) (event.Events, error) {
|
||||
func (w *wrapper) Read(ctx context.Context, after int64, count int64) (event.Events, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
return w.up.Read(ctx, pos, count)
|
||||
return w.up.Read(ctx, after, count)
|
||||
}
|
||||
func (w *wrapper) Append(ctx context.Context, events event.Events, version uint64) (uint64, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
|
||||
@@ -142,11 +142,11 @@ type wrapper struct {
|
||||
|
||||
var _ driver.EventLog = (*wrapper)(nil)
|
||||
|
||||
func (w *wrapper) Read(ctx context.Context, pos int64, count int64) (event.Events, error) {
|
||||
func (w *wrapper) Read(ctx context.Context, after int64, count int64) (event.Events, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
return w.up.Read(ctx, pos, count)
|
||||
return w.up.Read(ctx, after, count)
|
||||
}
|
||||
func (w *wrapper) Append(ctx context.Context, events event.Events, version uint64) (uint64, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
|
||||
25
pkg/es/es.go
25
pkg/es/es.go
@@ -121,7 +121,6 @@ func (es *EventStore) Save(ctx context.Context, agg event.Aggregate) (uint64, er
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
Mes_save.Add(ctx, 1)
|
||||
span.SetAttributes(
|
||||
attribute.String("agg.type", event.TypeOf(agg)),
|
||||
attribute.String("agg.streamID", agg.StreamID()),
|
||||
@@ -137,6 +136,7 @@ func (es *EventStore) Save(ctx context.Context, agg event.Aggregate) (uint64, er
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
Mes_save.Add(ctx, int64(count))
|
||||
|
||||
agg.Commit()
|
||||
return count, err
|
||||
@@ -145,8 +145,6 @@ func (es *EventStore) Load(ctx context.Context, agg event.Aggregate) error {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
Mes_load.Add(ctx, 1)
|
||||
|
||||
span.SetAttributes(
|
||||
attribute.String("agg.type", event.TypeOf(agg)),
|
||||
attribute.String("agg.streamID", agg.StreamID()),
|
||||
@@ -162,6 +160,7 @@ func (es *EventStore) Load(ctx context.Context, agg event.Aggregate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
Mes_load.Add(ctx, events.Count())
|
||||
event.Append(agg, events...)
|
||||
|
||||
span.SetAttributes(
|
||||
@@ -170,20 +169,25 @@ func (es *EventStore) Load(ctx context.Context, agg event.Aggregate) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
func (es *EventStore) Read(ctx context.Context, streamID string, pos, count int64) (event.Events, error) {
|
||||
func (es *EventStore) Read(ctx context.Context, streamID string, after, count int64) (event.Events, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
Mes_read.Add(ctx, 1)
|
||||
span.SetAttributes(
|
||||
attribute.String("ev.streamID", streamID),
|
||||
attribute.String("streamID", streamID),
|
||||
attribute.Int64("after", after),
|
||||
attribute.Int64("count", count),
|
||||
)
|
||||
|
||||
l, err := es.Driver.EventLog(ctx, streamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.Read(ctx, pos, count)
|
||||
|
||||
events, err := l.Read(ctx, after, count)
|
||||
Mes_read.Add(ctx, events.Count())
|
||||
|
||||
return events, err
|
||||
}
|
||||
func (es *EventStore) Append(ctx context.Context, streamID string, events event.Events) (uint64, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
@@ -203,6 +207,9 @@ func (es *EventStore) Append(ctx context.Context, streamID string, events event.
|
||||
func (es *EventStore) FirstIndex(ctx context.Context, streamID string) (uint64, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.String("ev.streamID", streamID),
|
||||
)
|
||||
|
||||
l, err := es.Driver.EventLog(ctx, streamID)
|
||||
if err != nil {
|
||||
@@ -213,6 +220,9 @@ func (es *EventStore) FirstIndex(ctx context.Context, streamID string) (uint64,
|
||||
func (es *EventStore) LastIndex(ctx context.Context, streamID string) (uint64, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.String("ev.streamID", streamID),
|
||||
)
|
||||
|
||||
l, err := es.Driver.EventLog(ctx, streamID)
|
||||
if err != nil {
|
||||
@@ -248,6 +258,7 @@ var ErrNoDriver = errors.New("no driver")
|
||||
var ErrWrongVersion = errors.New("wrong version")
|
||||
var ErrShouldExist = event.ErrShouldExist
|
||||
var ErrShouldNotExist = event.ErrShouldNotExist
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
type PA[T any] interface {
|
||||
event.Aggregate
|
||||
|
||||
@@ -20,5 +20,9 @@ type Event implements Edge @goModel(model: "github.com/sour-is/ev/pkg/es.GQLEven
|
||||
eventID: String!
|
||||
values: Map!
|
||||
bytes: String!
|
||||
type: String!
|
||||
created: Time!
|
||||
meta: Meta!
|
||||
|
||||
linked: Event
|
||||
}
|
||||
@@ -60,6 +60,9 @@ func (lis Events) StreamID() string {
|
||||
func (lis Events) SetStreamID(streamID string) {
|
||||
SetStreamID(streamID, lis...)
|
||||
}
|
||||
func (lis Events) Count() int64 {
|
||||
return int64(len(lis))
|
||||
}
|
||||
func (lis Events) First() Event {
|
||||
if len(lis) == 0 {
|
||||
return NilEvent
|
||||
|
||||
@@ -261,11 +261,30 @@ func Values(e Event) map[string]any {
|
||||
continue
|
||||
}
|
||||
|
||||
omitempty := false
|
||||
field := v.FieldByIndex(idx.Index)
|
||||
|
||||
name := idx.Name
|
||||
if n, ok := idx.Tag.Lookup("json"); ok {
|
||||
name = n
|
||||
var (
|
||||
opt string
|
||||
found bool
|
||||
)
|
||||
|
||||
name, opt, found = strings.Cut(n, ",")
|
||||
if name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if found {
|
||||
if strings.Contains(opt, "omitempty") {
|
||||
omitempty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if omitempty && field.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
m[name] = field.Interface()
|
||||
|
||||
@@ -2,6 +2,7 @@ package es
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -15,13 +16,18 @@ type EventResolver interface {
|
||||
Events(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error)
|
||||
EventAdded(ctx context.Context, streamID string, after int64) (<-chan *GQLEvent, error)
|
||||
}
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
var esKey = contextKey{"event-store"}
|
||||
|
||||
func (es *EventStore) Events(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
lis, err := es.Read(ctx, streamID, paging.GetIdx(0), paging.GetCount(30))
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
span.RecordError(err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,6 +113,14 @@ func (e *EventStore) EventAdded(ctx context.Context, streamID string, after int6
|
||||
return ch, nil
|
||||
}
|
||||
func (*EventStore) RegisterHTTP(*http.ServeMux) {}
|
||||
func (e *EventStore) GetMiddleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(gql.ToContext(r.Context(), esKey, e))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type GQLEvent struct {
|
||||
e event.Event
|
||||
@@ -118,6 +132,12 @@ func (e *GQLEvent) ID() string {
|
||||
func (e *GQLEvent) EventID() string {
|
||||
return e.e.EventMeta().GetEventID()
|
||||
}
|
||||
func (e *GQLEvent) Type() string {
|
||||
return event.TypeOf(e.e)
|
||||
}
|
||||
func (e *GQLEvent) Created() time.Time {
|
||||
return e.e.EventMeta().Created()
|
||||
}
|
||||
func (e *GQLEvent) Values() map[string]interface{} {
|
||||
return event.Values(e.e)
|
||||
}
|
||||
@@ -129,4 +149,18 @@ func (e *GQLEvent) Meta() *event.Meta {
|
||||
meta := e.e.EventMeta()
|
||||
return &meta
|
||||
}
|
||||
func (e *GQLEvent) Linked(ctx context.Context) (*GQLEvent, error) {
|
||||
values := event.Values(e.e)
|
||||
streamID, ok := values["stream_id"].(string)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
pos, ok := values["pos"].(uint64)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
events, err := gql.FromContext[contextKey, *EventStore](ctx, esKey).Read(ctx, streamID, int64(pos)-1, 1)
|
||||
return &GQLEvent{e: events.First()}, err
|
||||
}
|
||||
func (e *GQLEvent) IsEdge() {}
|
||||
|
||||
@@ -6,8 +6,9 @@ type Connection @goModel(model: "github.com/sour-is/ev/pkg/gql.Connection") {
|
||||
edges: [Edge!]!
|
||||
}
|
||||
input PageInput @goModel(model: "github.com/sour-is/ev/pkg/gql.PageInput") {
|
||||
idx: Int = 0
|
||||
count: Int = 30
|
||||
after: Int = 0
|
||||
before: Int
|
||||
count: Int = 30
|
||||
}
|
||||
type PageInfo @goModel(model: "github.com/sour-is/ev/pkg/gql.PageInfo") {
|
||||
next: Boolean!
|
||||
|
||||
@@ -38,19 +38,30 @@ type PageInfo struct {
|
||||
}
|
||||
|
||||
type PageInput struct {
|
||||
Idx *int64 `json:"idx"`
|
||||
Count *int64 `json:"count"`
|
||||
After *int64 `json:"after"`
|
||||
Before *int64 `json:"before"`
|
||||
Count *int64 `json:"count"`
|
||||
}
|
||||
|
||||
func (p *PageInput) GetIdx(v int64) int64 {
|
||||
if p == nil || p.Idx == nil {
|
||||
return v
|
||||
if p == nil {
|
||||
// pass
|
||||
} else if p.Before != nil {
|
||||
return (*p.Before)
|
||||
} else if p.After != nil {
|
||||
return *p.After
|
||||
}
|
||||
return *p.Idx
|
||||
|
||||
return v
|
||||
}
|
||||
func (p *PageInput) GetCount(v int64) int64 {
|
||||
if p == nil || p.Count == nil {
|
||||
return v
|
||||
} else if p.Before != nil {
|
||||
return -(*p.Count)
|
||||
} else if p.After != nil {
|
||||
return *p.Count
|
||||
}
|
||||
|
||||
return *p.Count
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user