Further scaffolding
This commit is contained in:
3
cmd/datad/main.go
Normal file
3
cmd/datad/main.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
91
core/data/bus.go
Normal file
91
core/data/bus.go
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
9
core/data/descriptor.go
Normal 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
18
core/data/egress.go
Normal 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
13
core/data/envelope.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package data ...
|
||||
package data
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Envelope struct {
|
||||
SendTime time.Time
|
||||
|
||||
Descriptor Descriptor
|
||||
Payload any
|
||||
}
|
||||
@@ -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
12
core/data/events/bar.go
Normal 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
|
||||
}
|
||||
6
core/data/events/custom.go
Normal file
6
core/data/events/custom.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package events
|
||||
|
||||
type Custom struct {
|
||||
Bytes []byte
|
||||
ContentType string
|
||||
}
|
||||
22
core/data/events/domain.go
Normal file
22
core/data/events/domain.go
Normal 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
|
||||
)
|
||||
20
core/data/events/mbodelta.go
Normal file
20
core/data/events/mbodelta.go
Normal 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
|
||||
)
|
||||
14
core/data/events/mbosnapshot.go
Normal file
14
core/data/events/mbosnapshot.go
Normal 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
|
||||
}
|
||||
8
core/data/events/mbpdelta.go
Normal file
8
core/data/events/mbpdelta.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package events
|
||||
|
||||
type MBPDelta struct {
|
||||
Side Side
|
||||
Price float64
|
||||
Size float64
|
||||
Seq uint64
|
||||
}
|
||||
13
core/data/events/mbpsnapshot.go
Normal file
13
core/data/events/mbpsnapshot.go
Normal 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
|
||||
}
|
||||
8
core/data/events/quote.go
Normal file
8
core/data/events/quote.go
Normal 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
16
core/data/events/trade.go
Normal 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
34
core/data/ingress.go
Normal 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
14
core/data/processor.go
Normal 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
1
core/data/registry.go
Normal file
@@ -0,0 +1 @@
|
||||
package data
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
16
core/data/router.go
Normal 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 }
|
||||
2
go.mod
2
go.mod
@@ -1,3 +1,5 @@
|
||||
module gitlab.michelsen.id/phillmichelsen/tessera
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require github.com/google/uuid v1.6.0 // indirect
|
||||
|
||||
Reference in New Issue
Block a user