Further scaffolding

This commit is contained in:
2025-11-27 20:34:34 +08:00
parent c52b2f7aa6
commit f1e5937978
28 changed files with 322 additions and 143 deletions

3
cmd/datad/main.go Normal file
View File

@@ -0,0 +1,3 @@
package main
func main() {}

91
core/data/bus.go Normal file
View File

@@ -0,0 +1,91 @@
package data
import (
"sync"
"github.com/google/uuid"
)
type Bus interface {
Publish(id uuid.UUID, env Envelope) error
Subscribe(id uuid.UUID) (<-chan Envelope, func(), error)
}
type InMemBus struct {
mu sync.RWMutex
subs map[uuid.UUID][]chan Envelope
publishChan chan published
}
type published struct {
id uuid.UUID
env Envelope
}
const (
busBufferSize = 256 // buffer size for publishChan
subscriberBufferSize = 128 // buffer size for each subscriber channel
)
func NewInMemBus() *InMemBus {
b := &InMemBus{
subs: make(map[uuid.UUID][]chan Envelope),
publishChan: make(chan published, busBufferSize),
}
go b.run()
return b
}
func (b *InMemBus) Publish(id uuid.UUID, env Envelope) error {
b.publishChan <- published{id: id, env: env}
return nil
}
func (b *InMemBus) Subscribe(id uuid.UUID) (<-chan Envelope, func(), error) {
ch := make(chan Envelope, subscriberBufferSize)
b.mu.Lock()
b.subs[id] = append(b.subs[id], ch)
b.mu.Unlock()
cancel := func() {
b.mu.Lock()
defer b.mu.Unlock()
chans := b.subs[id]
for i, c := range chans {
if c == ch {
// swap-delete
chans[i] = chans[len(chans)-1]
chans = chans[:len(chans)-1]
break
}
}
if len(chans) == 0 {
delete(b.subs, id)
} else {
b.subs[id] = chans
}
}
return ch, cancel, nil
}
func (b *InMemBus) run() {
for pub := range b.publishChan {
id := pub.id
b.mu.RLock()
chans := b.subs[id]
for _, ch := range chans {
select {
case ch <- pub.env:
default:
<-ch
ch <- pub.env
}
}
b.mu.RUnlock()
}
}

View File

@@ -1,33 +0,0 @@
// Package data ...
package data
import "time"
type Envelope struct {
Identifier string
Timestamp time.Time
Sequence uint64
Payload []byte
}
type Source interface {
Start(config any) error
Stop()
Name() string
Subscribe(key string) (<-chan Envelope, error)
Unsubscribe(key string) error
IsValidKey(key string) bool
GetSubscriptions() []string
}
type Processor interface {
Start(config any, send chan<- Envelope, receive <-chan Envelope) error
Stop()
}
type Sink interface {
Start(config any, receive <-chan Envelope) error
Stop()
}

9
core/data/descriptor.go Normal file
View File

@@ -0,0 +1,9 @@
package data
type Descriptor struct {
Type string
Key string
Schema string
Attributes map[string]string
Tags []string
}

18
core/data/egress.go Normal file
View File

@@ -0,0 +1,18 @@
package data
import (
"context"
"github.com/google/uuid"
)
type Egress interface {
Name() string
Configure(cfg map[string]any) error
Connect(ctx context.Context, actions EgresActions) error
Disconnect(ctx context.Context) error
}
type EgresActions interface {
Subscribe(ctx context.Context, namespace string, id uuid.UUID) (<-chan Envelope, func(), error)
}

13
core/data/envelope.go Normal file
View File

@@ -0,0 +1,13 @@
// Package data ...
package data
import (
"time"
)
type Envelope struct {
SendTime time.Time
Descriptor Descriptor
Payload any
}

View File

@@ -1,10 +0,0 @@
package data
import "errors"
var (
ErrUnknownKey = errors.New("unknown key")
ErrAlreadySubscribed = errors.New("already subscribed")
ErrNotSubscribed = errors.New("not subscribed")
ErrDroppedMessage = errors.New("dropped message: buffer full")
)

12
core/data/events/bar.go Normal file
View File

@@ -0,0 +1,12 @@
package events
import "time"
type Bar struct {
Open float64
High float64
Low float64
Close float64
Volume float64
Interval time.Duration
}

View File

@@ -0,0 +1,6 @@
package events
type Custom struct {
Bytes []byte
ContentType string
}

View File

@@ -0,0 +1,22 @@
// Package events ...
package events
type DataType uint8
const (
TradeType DataType = iota
QuoteType
BarType
MBPDeltaType
MBPSnapshotType
MBODeltaType
MBOSnapshotType
CustomType
)
type Side uint8
const (
Bid Side = iota
Ask
)

View File

@@ -0,0 +1,20 @@
package events
type MBODelta struct {
Operation MBOOrderOp
OrderID string
Side Side
Price float64
Size float64
IsMaker bool
Seq uint64
ParentID string
}
type MBOOrderOp uint8
const (
OrderAdd MBOOrderOp = iota
OrderMod
OrderDel
)

View File

@@ -0,0 +1,14 @@
package events
type MBOSnapshot struct {
Orders []OrderEntry
Seq uint64
}
type OrderEntry struct {
OrderID string
Side Side
Price float64
Size float64
IsMaker bool
}

View File

@@ -0,0 +1,8 @@
package events
type MBPDelta struct {
Side Side
Price float64
Size float64
Seq uint64
}

View File

@@ -0,0 +1,13 @@
package events
type MBPSnapshot struct {
Bids []PriceLevel
Asks []PriceLevel
Depth int
Seq uint64
}
type PriceLevel struct {
Price float64
Size float64
}

View File

@@ -0,0 +1,8 @@
package events
type Quote struct {
BidPrice float64
BidSize float64
AskPrice float64
AskSize float64
}

16
core/data/events/trade.go Normal file
View File

@@ -0,0 +1,16 @@
package events
type Trade struct {
Price float64
Qty float64
Aggressor AggressorSide
TradeID string
}
type AggressorSide uint8
const (
AggUnknown AggressorSide = iota
AggBuy
AggSell
)

34
core/data/ingress.go Normal file
View File

@@ -0,0 +1,34 @@
package data
import (
"context"
"time"
"github.com/google/uuid"
)
type Ingress interface {
Name() string
Configure(cfg map[string]any) error
Connect(ctx context.Context, actions IngressActions) error
Disconnect(ctx context.Context) error
}
type IngressActions interface {
Emit(namespace string, id uuid.UUID, envelope Envelope) error
EmitBatch(namespace string, id uuid.UUID, envelopes []Envelope) error
}
type LiveIngress interface {
Subscribe(ctx context.Context, key ...string) error
Unsubscribe(ctx context.Context, key ...string) error
ListAvailableKeys(ctx context.Context) []string
IsValidKey(ctx context.Context, key string) bool
}
type HistoricalIngress interface {
Fetch(ctx context.Context, key string, start time.Time, end time.Time) ([]Envelope, error)
ListAvailableKeys(ctx context.Context) []string
IsValidKey(ctx context.Context, key string) bool
// some other method that gives the avalible time period for a key
}

14
core/data/processor.go Normal file
View File

@@ -0,0 +1,14 @@
package data
import "context"
type Processor interface {
Name() string
Configure(cfg map[string]any) error
Run(ctx context.Context, actions ProcessorActions) error
}
type ProcessorActions interface {
IngressActions
EgresActions
}

1
core/data/registry.go Normal file
View File

@@ -0,0 +1 @@
package data

View File

@@ -1,34 +0,0 @@
// Package registry ...
package registry
import (
"fmt"
"sync"
"gitlab.michelsen.id/phillmichelsen/tessera/core/data"
)
type ProcessorFactory func(string) (data.Processor, error)
type Processors struct {
mu sync.RWMutex
m map[string]ProcessorFactory
}
func NewProcessors() *Processors { return &Processors{m: make(map[string]ProcessorFactory)} }
func (r *Processors) Register(name string, f ProcessorFactory) {
r.mu.Lock()
defer r.mu.Unlock()
r.m[name] = f
}
func (r *Processors) New(name string, params string) (data.Processor, error) {
r.mu.RLock()
f, ok := r.m[name]
r.mu.RUnlock()
if !ok {
return nil, fmt.Errorf("unknown processor: %s", name)
}
return f(params)
}

View File

@@ -1,33 +0,0 @@
package registry
import (
"fmt"
"sync"
"gitlab.michelsen.id/phillmichelsen/tessera/core/data"
)
type SinkFactory func(string) (data.Sink, error)
type Sinks struct {
mu sync.RWMutex
m map[string]SinkFactory
}
func NewSinks() *Sinks { return &Sinks{m: make(map[string]SinkFactory)} }
func (r *Sinks) Register(name string, f SinkFactory) {
r.mu.Lock()
defer r.mu.Unlock()
r.m[name] = f
}
func (r *Sinks) New(name string, params string) (data.Sink, error) {
r.mu.RLock()
f, ok := r.m[name]
r.mu.RUnlock()
if !ok {
return nil, fmt.Errorf("unknown sink: %s", name)
}
return f(params)
}

View File

@@ -1,33 +0,0 @@
package registry
import (
"fmt"
"sync"
"gitlab.michelsen.id/phillmichelsen/tessera/core/data"
)
type SourceFactory func(string) (data.Source, error)
type Sources struct {
mu sync.RWMutex
m map[string]SourceFactory
}
func NewSources() *Sources { return &Sources{m: make(map[string]SourceFactory)} }
func (r *Sources) Register(name string, f SourceFactory) {
r.mu.Lock()
defer r.mu.Unlock()
r.m[name] = f
}
func (r *Sources) New(name string, params string) (data.Source, error) {
r.mu.RLock()
f, ok := r.m[name]
r.mu.RUnlock()
if !ok {
return nil, fmt.Errorf("unknown source: %s", name)
}
return f(params)
}

16
core/data/router.go Normal file
View File

@@ -0,0 +1,16 @@
package data
import "github.com/google/uuid"
type Router struct {
busses map[string]Bus
}
func NewRouter() *Router {
return &Router{
busses: make(map[string]Bus),
}
}
func (r *Router) Emit(namespace string, id uuid.UUID, envelope Envelope) error { return nil }
func (r *Router) EmitBatch(namespace string, id uuid.UUID, enveloeps []Envelope) error { return nil }

View File

2
go.mod
View File

@@ -1,3 +1,5 @@
module gitlab.michelsen.id/phillmichelsen/tessera
go 1.25.1
require github.com/google/uuid v1.6.0 // indirect

2
go.sum
View File

@@ -0,0 +1,2 @@
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=