feat: add logs/metrics/tracing
This commit is contained in:
parent
fd97f2ff17
commit
ea2186a034
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,6 @@
|
||||||
.vscode/
|
.vscode/
|
||||||
data/
|
data/
|
||||||
|
.gitsecret/keys/random_seed
|
||||||
|
!*.secret
|
||||||
|
local.mk
|
||||||
|
logzio.yml
|
||||||
|
|
BIN
.gitsecret/keys/pubring.kbx
Normal file
BIN
.gitsecret/keys/pubring.kbx
Normal file
Binary file not shown.
BIN
.gitsecret/keys/pubring.kbx~
Normal file
BIN
.gitsecret/keys/pubring.kbx~
Normal file
Binary file not shown.
BIN
.gitsecret/keys/trustdb.gpg
Normal file
BIN
.gitsecret/keys/trustdb.gpg
Normal file
Binary file not shown.
2
.gitsecret/paths/mapping.cfg
Normal file
2
.gitsecret/paths/mapping.cfg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
logzio.yml
|
||||||
|
local.mk
|
3
Makefile
3
Makefile
|
@ -1,5 +1,6 @@
|
||||||
export EV_DATA = mem:
|
export EV_DATA = mem:
|
||||||
export EV_HTTP = :8080
|
export EV_HTTP = :8080
|
||||||
|
-include local.mk
|
||||||
|
|
||||||
run: gen
|
run: gen
|
||||||
go run .
|
go run .
|
||||||
|
@ -20,7 +21,7 @@ endif
|
||||||
gqlgen
|
gqlgen
|
||||||
|
|
||||||
load:
|
load:
|
||||||
watch -n .1 "http POST localhost:8080/event/asdf/test a=b one=1 two:='{\"v\":2}' | jq"
|
watch -n .1 "http POST localhost:8080/inbox/asdf/test a=b one=1 two:='{\"v\":2}' | jq"
|
||||||
|
|
||||||
bi:
|
bi:
|
||||||
go build .
|
go build .
|
||||||
|
|
42
go.mod
42
go.mod
|
@ -5,16 +5,53 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.13
|
github.com/99designs/gqlgen v0.17.13
|
||||||
github.com/tidwall/wal v1.1.7
|
github.com/tidwall/wal v1.1.7
|
||||||
github.com/vektah/gqlparser/v2 v2.4.6
|
github.com/vektah/gqlparser/v2 v2.4.7
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
|
github.com/beeker1121/goque v2.1.0+incompatible // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.3.1 // indirect
|
github.com/logzio/go-metrics-sdk v1.0.0 // indirect
|
||||||
|
github.com/logzio/logzio-go v1.0.6 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/prometheus/prometheus v1.8.2-0.20210928085443-fafb309d4027 // indirect
|
||||||
|
github.com/ravilushqa/otelgqlgen v0.9.0 // indirect
|
||||||
github.com/rs/cors v1.8.2 // indirect
|
github.com/rs/cors v1.8.2 // indirect
|
||||||
|
github.com/shirou/gopsutil/v3 v3.22.3 // indirect
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.opentelemetry.io/contrib v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.30.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/runtime v0.30.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v0.27.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v0.27.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.9.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v0.18.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
|
||||||
|
google.golang.org/grpc v1.46.2 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -24,4 +61,5 @@ require (
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tidwall/tinylru v1.1.0 // indirect
|
github.com/tidwall/tinylru v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0
|
||||||
)
|
)
|
||||||
|
|
232
logger.go
Normal file
232
logger.go
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
metricsExporter "github.com/logzio/go-metrics-sdk"
|
||||||
|
"github.com/logzio/logzio-go"
|
||||||
|
|
||||||
|
"github.com/go-logr/stdr"
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/runtime"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||||
|
"go.opentelemetry.io/otel/metric/global"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/metric/controller/basic"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init(ctx context.Context) {
|
||||||
|
stop := []func() error {
|
||||||
|
initLogger(),
|
||||||
|
initMetrics(),
|
||||||
|
initTracing(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse(stop)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
for _, fn := range stop {
|
||||||
|
if err := fn(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
type logzwriter struct {
|
||||||
|
pkg string
|
||||||
|
goversion string
|
||||||
|
hostname string
|
||||||
|
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logzwriter) Write(b []byte) (int, error) {
|
||||||
|
i := 0
|
||||||
|
for _, sp := range bytes.Split(b, []byte("\n")) {
|
||||||
|
msg := struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
GoVersion string `json:"go_version"`
|
||||||
|
Package string `json:"pkg"`
|
||||||
|
App string `json:"app"`
|
||||||
|
}{
|
||||||
|
Message: strings.TrimSpace(string(sp)),
|
||||||
|
Host: l.hostname,
|
||||||
|
GoVersion: l.goversion,
|
||||||
|
Package: l.pkg,
|
||||||
|
App: app_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Message == "" || strings.HasPrefix(msg.Message, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
j, err := l.w.Write(b)
|
||||||
|
|
||||||
|
i += j
|
||||||
|
if err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLogger() func() error {
|
||||||
|
log.SetPrefix("[" + app_name + "] ")
|
||||||
|
log.SetFlags(log.LstdFlags&^(log.Ldate|log.Ltime) | log.Lshortfile)
|
||||||
|
|
||||||
|
token := env("LOGZIO_LOG_TOKEN", "")
|
||||||
|
if token == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := logzio.New(
|
||||||
|
token,
|
||||||
|
// logzio.SetDebug(os.Stderr),
|
||||||
|
logzio.SetUrl(env("LOGZIO_LOG_URL", "https://listener.logz.io:8071")),
|
||||||
|
logzio.SetDrainDuration(time.Second*5),
|
||||||
|
logzio.SetTempDirectory(env("LOGZIO_DIR", os.TempDir())),
|
||||||
|
logzio.SetCheckDiskSpace(true),
|
||||||
|
logzio.SetDrainDiskThreshold(70),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w := io.MultiWriter(os.Stderr, lzw(l))
|
||||||
|
log.SetOutput(w)
|
||||||
|
otel.SetLogger(stdr.New(log.Default()))
|
||||||
|
|
||||||
|
return func() error { l.Stop(); return nil }
|
||||||
|
}
|
||||||
|
func lzw(l io.Writer) io.Writer {
|
||||||
|
lz := &logzwriter{
|
||||||
|
w: l,
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
lz.goversion = info.GoVersion
|
||||||
|
lz.pkg = info.Path
|
||||||
|
}
|
||||||
|
if hostname, err := os.Hostname(); err == nil {
|
||||||
|
lz.hostname = hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
return lz
|
||||||
|
}
|
||||||
|
|
||||||
|
func initMetrics() func() error {
|
||||||
|
goversion := ""
|
||||||
|
pkg := ""
|
||||||
|
host := ""
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
goversion = info.GoVersion
|
||||||
|
pkg = info.Path
|
||||||
|
}
|
||||||
|
if h, err := os.Hostname(); err == nil {
|
||||||
|
host = h
|
||||||
|
}
|
||||||
|
|
||||||
|
config := metricsExporter.Config{
|
||||||
|
LogzioMetricsListener: env("LOGZIO_METRIC_URL", "https://listener.logz.io:8053"),
|
||||||
|
LogzioMetricsToken: env("LOGZIO_METRIC_TOKEN", ""),
|
||||||
|
RemoteTimeout: 30 * time.Second,
|
||||||
|
PushInterval: 5 * time.Second,
|
||||||
|
}
|
||||||
|
if config.LogzioMetricsToken == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the `config` instance from last step.
|
||||||
|
|
||||||
|
cont, err := metricsExporter.InstallNewPipeline(
|
||||||
|
config,
|
||||||
|
basic.WithCollectPeriod(30*time.Second),
|
||||||
|
basic.WithResource(
|
||||||
|
resource.NewWithAttributes(
|
||||||
|
semconv.SchemaURL,
|
||||||
|
attribute.String("app", app_name),
|
||||||
|
attribute.String("host", host),
|
||||||
|
attribute.String("go_version", goversion),
|
||||||
|
attribute.String("pkg", pkg),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
global.SetMeterProvider(cont)
|
||||||
|
runtime.Start()
|
||||||
|
|
||||||
|
return func() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
return cont.Stop(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTracing(ctx context.Context) func() error {
|
||||||
|
res, err := resource.New(ctx,
|
||||||
|
resource.WithAttributes(
|
||||||
|
semconv.ServiceNameKey.String("sour.is-ev"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
log.Println(wrap(err, "failed to create trace resource"))
|
||||||
|
|
||||||
|
traceExporter, err := otlptracehttp.New(ctx,
|
||||||
|
otlptracehttp.WithInsecure(),
|
||||||
|
otlptracehttp.WithEndpoint("localhost:4318"),
|
||||||
|
)
|
||||||
|
log.Println(wrap(err, "failed to create trace exporter"))
|
||||||
|
|
||||||
|
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
|
||||||
|
tracerProvider := sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
sdktrace.WithResource(res),
|
||||||
|
sdktrace.WithSpanProcessor(bsp),
|
||||||
|
)
|
||||||
|
otel.SetTracerProvider(tracerProvider)
|
||||||
|
otel.SetTextMapPropagator(propagation.TraceContext{})
|
||||||
|
return func() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
return wrap(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrap(err error, s string) error {
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(s, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func reverse[T any](s []T) {
|
||||||
|
first, last := 0, len(s) - 1
|
||||||
|
for first < last {
|
||||||
|
s[first], s[last] = s[last], s[first]
|
||||||
|
first++
|
||||||
|
last--
|
||||||
|
}
|
||||||
|
}
|
12
main.go
12
main.go
|
@ -10,7 +10,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/handler"
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
|
"github.com/ravilushqa/otelgqlgen"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
|
"go.opentelemetry.io/otel/metric/global"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/sour-is/ev/api/gql_ev"
|
"github.com/sour-is/ev/api/gql_ev"
|
||||||
|
@ -24,12 +26,21 @@ import (
|
||||||
"github.com/sour-is/ev/pkg/playground"
|
"github.com/sour-is/ev/pkg/playground"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const app_name string = "sour.is-ev"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
}()
|
}()
|
||||||
|
Init(ctx)
|
||||||
|
|
||||||
|
up, err := global.GetMeterProvider().Meter(app_name).NewFloat64UpDownCounter("up")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
up.Add(ctx, 1.0)
|
||||||
|
|
||||||
if err := run(ctx); err != nil {
|
if err := run(ctx); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -51,6 +62,7 @@ func run(ctx context.Context) error {
|
||||||
|
|
||||||
res := graph.New(gql_ev.New(es))
|
res := graph.New(gql_ev.New(es))
|
||||||
gql := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: res}))
|
gql := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: res}))
|
||||||
|
gql.Use(otelgqlgen.Middleware())
|
||||||
|
|
||||||
s := http.Server{
|
s := http.Server{
|
||||||
Addr: env("EV_HTTP", ":8080"),
|
Addr: env("EV_HTTP", ":8080"),
|
||||||
|
|
|
@ -2,7 +2,6 @@ package streamer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/sour-is/ev/pkg/es"
|
"github.com/sour-is/ev/pkg/es"
|
||||||
"github.com/sour-is/ev/pkg/es/driver"
|
"github.com/sour-is/ev/pkg/es/driver"
|
||||||
|
@ -56,19 +55,15 @@ func (s *streamer) Subscribe(ctx context.Context, streamID string, start int64)
|
||||||
size: es.AllEvents,
|
size: es.AllEvents,
|
||||||
})
|
})
|
||||||
sub.unsub = s.delete(streamID, sub)
|
sub.unsub = s.delete(streamID, sub)
|
||||||
log.Println("start ", sub)
|
|
||||||
|
|
||||||
return sub, s.state.Modify(ctx, func(state *state) error {
|
return sub, s.state.Modify(ctx, func(state *state) error {
|
||||||
state.subscribers[streamID] = append(state.subscribers[streamID], sub)
|
state.subscribers[streamID] = append(state.subscribers[streamID], sub)
|
||||||
log.Println("add ", len(state.subscribers[streamID]))
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func (s *streamer) Send(ctx context.Context, streamID string, events event.Events) error {
|
func (s *streamer) Send(ctx context.Context, streamID string, events event.Events) error {
|
||||||
return s.state.Modify(ctx, func(state *state) error {
|
return s.state.Modify(ctx, func(state *state) error {
|
||||||
log.Println("trigger ", len(state.subscribers[streamID]))
|
|
||||||
for _, sub := range state.subscribers[streamID] {
|
for _, sub := range state.subscribers[streamID] {
|
||||||
log.Println("trigg ", sub)
|
|
||||||
err := sub.position.Modify(ctx, func(position *position) error {
|
err := sub.position.Modify(ctx, func(position *position) error {
|
||||||
position.size = int64(events.Last().EventMeta().Position - uint64(position.idx))
|
position.size = int64(events.Last().EventMeta().Position - uint64(position.idx))
|
||||||
|
|
||||||
|
@ -78,7 +73,9 @@ func (s *streamer) Send(ctx context.Context, streamID string, events event.Event
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil { return err }
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -89,7 +86,6 @@ func (s *streamer) delete(streamID string, sub *subscription) func(context.Conte
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("unsub ", s)
|
|
||||||
return s.state.Modify(ctx, func(state *state) error {
|
return s.state.Modify(ctx, func(state *state) error {
|
||||||
lis := state.subscribers[streamID]
|
lis := state.subscribers[streamID]
|
||||||
for i := range lis {
|
for i := range lis {
|
||||||
|
@ -150,8 +146,6 @@ type subscription struct {
|
||||||
|
|
||||||
func (s *subscription) Recv(ctx context.Context) bool {
|
func (s *subscription) Recv(ctx context.Context) bool {
|
||||||
var wait func(context.Context) bool
|
var wait func(context.Context) bool
|
||||||
log.Println("wait ", s)
|
|
||||||
defer log.Println("recv ", s)
|
|
||||||
err := s.position.Modify(ctx, func(position *position) error {
|
err := s.position.Modify(ctx, func(position *position) error {
|
||||||
if position.size == es.AllEvents {
|
if position.size == es.AllEvents {
|
||||||
return nil
|
return nil
|
||||||
|
@ -187,13 +181,10 @@ func (s *subscription) Events(ctx context.Context) (event.Events, error) {
|
||||||
var events event.Events
|
var events event.Events
|
||||||
return events, s.position.Modify(ctx, func(position *position) error {
|
return events, s.position.Modify(ctx, func(position *position) error {
|
||||||
var err error
|
var err error
|
||||||
log.Println("pos=", position, s)
|
|
||||||
events, err = s.events.Read(ctx, position.idx, position.size)
|
events, err = s.events.Read(ctx, position.idx, position.size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err, s)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("events=", len(events), s)
|
|
||||||
position.size = int64(len(events))
|
position.size = int64(len(events))
|
||||||
if len(events) > 0 {
|
if len(events) > 0 {
|
||||||
position.idx = int64(events.First().EventMeta().Position - 1)
|
position.idx = int64(events.First().EventMeta().Position - 1)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package es_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
|
|
||||||
func TestES(t *testing.T) {
|
func TestES(t *testing.T) {
|
||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
err := event.Register(ctx, &ValueSet{})
|
err := event.Register(ctx, &ValueSet{})
|
||||||
|
@ -22,6 +24,12 @@ func TestES(t *testing.T) {
|
||||||
|
|
||||||
memstore.Init(ctx)
|
memstore.Init(ctx)
|
||||||
|
|
||||||
|
_, err = es.Open(ctx, "mem")
|
||||||
|
is.True(errors.Is(err, es.ErrNoDriver))
|
||||||
|
|
||||||
|
_, err = es.Open(ctx, "bogo:")
|
||||||
|
is.True(errors.Is(err, es.ErrNoDriver))
|
||||||
|
|
||||||
es, err := es.Open(ctx, "mem:")
|
es, err := es.Open(ctx, "mem:")
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
|
@ -40,12 +48,25 @@ func TestES(t *testing.T) {
|
||||||
t.Log(thing.StreamVersion(), thing.Name, thing.Value)
|
t.Log(thing.StreamVersion(), thing.Name, thing.Value)
|
||||||
t.Log("Wrote: ", i)
|
t.Log("Wrote: ", i)
|
||||||
|
|
||||||
|
i, err = es.Append(ctx, "thing-time", event.NewEvents(&ValueSet{Value: "xxx"}))
|
||||||
|
is.NoErr(err)
|
||||||
|
is.Equal(i, uint64(1))
|
||||||
|
|
||||||
events, err := es.Read(ctx, "thing-time", -1, -11)
|
events, err := es.Read(ctx, "thing-time", -1, -11)
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
for i, e := range events {
|
for i, e := range events {
|
||||||
t.Logf("event %d %d - %v\n", i, e.EventMeta().Position, e)
|
t.Logf("event %d %d - %v\n", i, e.EventMeta().Position, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
first, err := es.FirstIndex(ctx, "thing-time")
|
||||||
|
is.NoErr(err)
|
||||||
|
is.Equal(first, uint64(1))
|
||||||
|
|
||||||
|
last, err := es.LastIndex(ctx, "thing-time")
|
||||||
|
is.NoErr(err)
|
||||||
|
is.Equal(last, uint64(2))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Thing struct {
|
type Thing struct {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user