mirror of
https://github.com/cirruslabs/orchard.git
synced 2026-10-20 09:39:01 +02:00
Refactorings for simplify readability (#35)
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
package concurrentmap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ConcurrentMap[T any] struct {
|
||||
nonConcurrentMap map[any]T
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewConcurrentMap[T any]() *ConcurrentMap[T] {
|
||||
return &ConcurrentMap[T]{
|
||||
nonConcurrentMap: map[any]T{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmap *ConcurrentMap[T]) Load(key any) (T, bool) {
|
||||
cmap.mtx.Lock()
|
||||
defer cmap.mtx.Unlock()
|
||||
|
||||
result, ok := cmap.nonConcurrentMap[key]
|
||||
|
||||
return result, ok
|
||||
}
|
||||
|
||||
func (cmap *ConcurrentMap[T]) Store(id any, value T) {
|
||||
cmap.mtx.Lock()
|
||||
defer cmap.mtx.Unlock()
|
||||
|
||||
cmap.nonConcurrentMap[id] = value
|
||||
}
|
||||
|
||||
func (cmap *ConcurrentMap[T]) Delete(key any) {
|
||||
cmap.mtx.Lock()
|
||||
defer cmap.mtx.Unlock()
|
||||
|
||||
delete(cmap.nonConcurrentMap, key)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package rendezvous
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/cirruslabs/orchard/internal/controller/rendezvous/concurrentmap"
|
||||
"net"
|
||||
)
|
||||
|
||||
var ErrInvalidToken = errors.New("invalid proxy token")
|
||||
|
||||
type Proxy struct {
|
||||
tokens *concurrentmap.ConcurrentMap[*TokenSlot]
|
||||
}
|
||||
|
||||
type TokenSlot struct {
|
||||
ctx context.Context
|
||||
ch chan net.Conn
|
||||
}
|
||||
|
||||
func NewProxy() *Proxy {
|
||||
return &Proxy{
|
||||
tokens: concurrentmap.NewConcurrentMap[*TokenSlot](),
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy *Proxy) Request(ctx context.Context, token string) (chan net.Conn, func()) {
|
||||
tokenSlot := &TokenSlot{
|
||||
ctx: ctx,
|
||||
ch: make(chan net.Conn),
|
||||
}
|
||||
|
||||
proxy.tokens.Store(token, tokenSlot)
|
||||
|
||||
return tokenSlot.ch, func() {
|
||||
proxy.tokens.Delete(token)
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy *Proxy) Respond(token string, conn net.Conn) (context.Context, error) {
|
||||
tokenSlot, ok := proxy.tokens.Load(token)
|
||||
if !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
tokenSlot.ch <- conn
|
||||
|
||||
return tokenSlot.ctx, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package rendezvous_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cirruslabs/orchard/internal/controller/rendezvous"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
expectedConn, _ := net.Pipe()
|
||||
|
||||
proxy := rendezvous.NewProxy()
|
||||
|
||||
token := uuid.New().String()
|
||||
|
||||
connCh, cancel := proxy.Request(ctx, token)
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
_, err := proxy.Respond(token, expectedConn)
|
||||
require.NoError(t, err)
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
actualConn := <-connCh
|
||||
require.Equal(t, expectedConn, actualConn)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package rendezvous
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/cirruslabs/orchard/internal/controller/rendezvous/concurrentmap"
|
||||
"github.com/cirruslabs/orchard/rpc"
|
||||
)
|
||||
|
||||
var ErrNoWorker = errors.New("no worker registered with this name")
|
||||
|
||||
type Watcher struct {
|
||||
workers *concurrentmap.ConcurrentMap[*WorkerSlot]
|
||||
}
|
||||
|
||||
type WorkerSlot struct {
|
||||
ctx context.Context
|
||||
ch chan *rpc.WatchFromController
|
||||
}
|
||||
|
||||
func NewWatcher() *Watcher {
|
||||
return &Watcher{
|
||||
workers: concurrentmap.NewConcurrentMap[*WorkerSlot](),
|
||||
}
|
||||
}
|
||||
|
||||
func (watcher *Watcher) Subscribe(ctx context.Context, worker string) (chan *rpc.WatchFromController, func()) {
|
||||
subCtx, cancel := context.WithCancel(ctx)
|
||||
workerCh := make(chan *rpc.WatchFromController)
|
||||
|
||||
watcher.workers.Store(worker, &WorkerSlot{
|
||||
ctx: subCtx,
|
||||
ch: workerCh,
|
||||
})
|
||||
|
||||
return workerCh, func() {
|
||||
watcher.workers.Delete(worker)
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func (watcher *Watcher) Notify(ctx context.Context, topic string, msg *rpc.WatchFromController) error {
|
||||
slot, ok := watcher.workers.Load(topic)
|
||||
if !ok {
|
||||
return fmt.Errorf("%w: %s", ErrNoWorker, topic)
|
||||
}
|
||||
|
||||
select {
|
||||
case slot.ch <- msg:
|
||||
return nil
|
||||
case <-slot.ctx.Done():
|
||||
return slot.ctx.Err()
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package rendezvous_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cirruslabs/orchard/internal/controller/rendezvous"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWatcher(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
watcher := rendezvous.NewWatcher()
|
||||
|
||||
var topic = uuid.New().String()
|
||||
|
||||
msgCh, cancel := watcher.Subscribe(context.Background(), topic)
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
require.NoError(t, watcher.Notify(ctx, topic, nil))
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
require.NoError(t, watcher.Notify(ctx, topic, nil))
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
fmt.Println("waiting for the message...")
|
||||
|
||||
<-msgCh
|
||||
|
||||
fmt.Println("received first message")
|
||||
|
||||
<-msgCh
|
||||
|
||||
fmt.Println("received second message")
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user