experiment: Serf transport for go-ds-crdt

This commit is contained in:
Pavel Sviderski
2024-08-07 13:51:53 +10:00
parent b4606b939b
commit e6d8a33928
5 changed files with 509 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
package main
import (
"context"
"fmt"
"github.com/hashicorp/serf/serf"
crdt "github.com/ipfs/go-ds-crdt"
"log/slog"
"time"
)
// Implements the Broadcaster interface.
type SerfBroadcaster struct {
ctx context.Context
serf *serf.Serf
nextCh chan []byte
}
// The broadcaster can be shut down by cancelling the given context. This must be done before Closing
// the crdt.Datastore, otherwise things may hang.
func NewSerfBroadcaster(ctx context.Context, serf *serf.Serf) *SerfBroadcaster {
return &SerfBroadcaster{
ctx: ctx,
serf: serf,
nextCh: make(chan []byte),
}
}
func (b *SerfBroadcaster) Broadcast(bytes []byte) error {
slog.Debug("Broadcasting head nodes to peers", "size", len(bytes))
// Other peers are not allowed to coalesce this event by name as the payload may differ.
// TODO: decode CRDTBroadcast from bytes and embed the nodes content in the payload as well to save round trip.
if err := b.serf.UserEvent("heads", bytes, false); err != nil {
return fmt.Errorf("broadcast heads event: %w", err)
}
return nil
}
func (b *SerfBroadcaster) Next() ([]byte, error) {
select {
case bytes := <-b.nextCh: // Blocks until a new heads event is received.
return bytes, nil
case <-b.ctx.Done():
return nil, crdt.ErrNoMoreBroadcast
}
}
func (b *SerfBroadcaster) HandleEvent(event serf.Event) {
select {
case <-b.ctx.Done():
return
default:
}
slog.Debug("Received event in broadcaster", "event", event)
userEvent, ok := event.(serf.UserEvent)
if !ok {
// Ignore non-user events.
return
}
if userEvent.Name != "heads" {
// Ignore non-heads user events.
return
}
start := time.Now()
select {
case b.nextCh <- userEvent.Payload:
slog.Debug("Handled heads event", "duration", time.Since(start))
case <-b.ctx.Done():
}
}
var _ crdt.Broadcaster = (*SerfBroadcaster)(nil)