filter ss events by account
This commit is contained in:
@@ -61,7 +61,7 @@ func (r *sseRouter) serveEvents(c *gin.Context) (response.Response, error) {
|
|||||||
hdr.Set("Cache-Control", "no-cache")
|
hdr.Set("Cache-Control", "no-cache")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
|
||||||
err := r.sse.Listen(ctx, func(ctx context.Context, e *sse.Event) error {
|
err := r.sse.Listen(ctx, acctID, func(ctx context.Context, e *sse.Event) error {
|
||||||
log.Debugf("sending event of type %s", e.Type)
|
log.Debugf("sending event of type %s", e.Type)
|
||||||
|
|
||||||
e.Write(w)
|
e.Write(w)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"ruben/inventory2/internal/logging"
|
"ruben/inventory2/internal/logging"
|
||||||
"ruben/inventory2/internal/server/sse"
|
"ruben/inventory2/internal/server/sse"
|
||||||
@@ -40,6 +41,7 @@ func (p *UpdateNotificationPublisher) Group(pathPattern string) *UpdateNotificat
|
|||||||
return &p2
|
return &p2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Publish must be applied AFTER a middleware puts in the Identity into the gin.Context.
|
||||||
func (p *UpdateNotificationPublisher) Publish(pathPattern string) gin.HandlerFunc {
|
func (p *UpdateNotificationPublisher) Publish(pathPattern string) gin.HandlerFunc {
|
||||||
trimBasePathSegs := getPathSegments(p.trimBasePath)
|
trimBasePathSegs := getPathSegments(p.trimBasePath)
|
||||||
trimmedBasePathPattern := path.Join(p.basePathPattern, pathPattern)
|
trimmedBasePathPattern := path.Join(p.basePathPattern, pathPattern)
|
||||||
@@ -85,12 +87,17 @@ func (p *UpdateNotificationPublisher) Publish(pathPattern string) gin.HandlerFun
|
|||||||
parentEvent = e
|
parentEvent = e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acctID := GetIdentity(c).Account.AccountID
|
||||||
for _, e := range events {
|
for _, e := range events {
|
||||||
go func() {
|
go func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := p.sse.Send(ctx, e, nil); err != nil {
|
if err := p.sse.Send(ctx, sse.Event{
|
||||||
|
AccountID: acctID,
|
||||||
|
Type: e,
|
||||||
|
Data: []byte(fmt.Sprintf(`{"eventType": %q}`, e)),
|
||||||
|
}); err != nil {
|
||||||
p.log.Errorf("failed to send sse event to listener: %v", err)
|
p.log.Errorf("failed to send sse event to listener: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
+30
-15
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -11,16 +12,16 @@ import (
|
|||||||
type (
|
type (
|
||||||
Queue struct {
|
Queue struct {
|
||||||
in chan Event
|
in chan Event
|
||||||
out map[int]chan Event
|
out map[int64]map[int]chan Event
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
prevID int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Event struct {
|
Event struct {
|
||||||
|
AccountID int64
|
||||||
Type string
|
Type string
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
@@ -30,7 +31,7 @@ func NewQueue() *Queue {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &Queue{
|
return &Queue{
|
||||||
in: make(chan Event),
|
in: make(chan Event),
|
||||||
out: make(map[int]chan Event),
|
out: make(map[int64]map[int]chan Event),
|
||||||
|
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
@@ -46,9 +47,13 @@ func (q *Queue) Start(ctx context.Context) error {
|
|||||||
// we're done piping events
|
// we're done piping events
|
||||||
return nil
|
return nil
|
||||||
case e := <-q.in:
|
case e := <-q.in:
|
||||||
// share event will all subscribers
|
if e.AccountID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// share event with listeners on the account
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
for _, out := range q.out {
|
for _, out := range q.out[e.AccountID] {
|
||||||
out <- e
|
out <- e
|
||||||
}
|
}
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
@@ -56,21 +61,34 @@ func (q *Queue) Start(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Listen(ctx context.Context, fn func(context.Context, *Event) error) error {
|
func (q *Queue) Listen(ctx context.Context, acctID int64, fn func(context.Context, *Event) error) error {
|
||||||
// create new out pipe and append it to the queue
|
// create new out pipe and append it to the queue
|
||||||
|
|
||||||
out := make(chan Event, 1)
|
out := make(chan Event, 1)
|
||||||
|
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
q.prevID += 1
|
outs := q.out[acctID]
|
||||||
id := q.prevID
|
if outs == nil {
|
||||||
q.out[id] = out
|
outs = make(map[int]chan Event)
|
||||||
|
q.out[acctID] = outs
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxID int
|
||||||
|
for n := range maps.Keys(outs) {
|
||||||
|
maxID = max(maxID, n)
|
||||||
|
}
|
||||||
|
id := maxID + 1
|
||||||
|
|
||||||
|
outs[id] = out
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
|
|
||||||
// delete the pipe when done listening
|
// delete the pipe when done listening
|
||||||
defer func() {
|
defer func() {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
delete(q.out, id)
|
delete(q.out[acctID], id)
|
||||||
|
if len(q.out[acctID]) == 0 {
|
||||||
|
delete(q.out, acctID)
|
||||||
|
}
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -91,7 +109,7 @@ func (q *Queue) Listen(ctx context.Context, fn func(context.Context, *Event) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Send(ctx context.Context, eventType string, data []byte) error {
|
func (q *Queue) Send(ctx context.Context, e Event) error {
|
||||||
select {
|
select {
|
||||||
case <-q.ctx.Done():
|
case <-q.ctx.Done():
|
||||||
// the queue has closed
|
// the queue has closed
|
||||||
@@ -100,10 +118,7 @@ func (q *Queue) Send(ctx context.Context, eventType string, data []byte) error {
|
|||||||
// sender ran out of time
|
// sender ran out of time
|
||||||
return fmt.Errorf("provided context canceled: %w", ctx.Err())
|
return fmt.Errorf("provided context canceled: %w", ctx.Err())
|
||||||
// push the event onto the queue
|
// push the event onto the queue
|
||||||
case q.in <- Event{
|
case q.in <- e:
|
||||||
Type: eventType,
|
|
||||||
Data: data,
|
|
||||||
}:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user