send all on results on amqp
All checks were successful
Build and Publish / Metadata (push) Successful in 9s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 1m20s
Build and Publish / BuildAndDeployArm64 (push) Successful in 4m2s

This commit is contained in:
matst80
2025-10-15 07:57:45 +02:00
parent 47adb12112
commit 8c2bcf5e75
5 changed files with 102 additions and 36 deletions

View File

@@ -9,42 +9,48 @@ import (
) )
type AmqpOrderHandler struct { type AmqpOrderHandler struct {
Url string conn *amqp.Connection
Connection *amqp.Connection
Channel *amqp.Channel
} }
func (h *AmqpOrderHandler) Connect() error { func NewAmqpOrderHandler(conn *amqp.Connection) *AmqpOrderHandler {
conn, err := amqp.Dial(h.Url) return &AmqpOrderHandler{
if err != nil { conn: conn,
return fmt.Errorf("failed to connect to RabbitMQ: %w", err)
} }
h.Connection = conn }
ch, err := conn.Channel() func (h *AmqpOrderHandler) DefineTopics() error {
ch, err := h.conn.Channel()
if err != nil { if err != nil {
return fmt.Errorf("failed to open a channel: %w", err) return fmt.Errorf("failed to open a channel: %w", err)
} }
h.Channel = ch defer ch.Close()
return nil err = ch.ExchangeDeclare(
} "orders", // name
"direct", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
if err != nil {
return fmt.Errorf("failed to declare an exchange: %w", err)
}
func (h *AmqpOrderHandler) Close() error {
if h.Channel != nil {
h.Channel.Close()
}
if h.Connection != nil {
return h.Connection.Close()
}
return nil return nil
} }
func (h *AmqpOrderHandler) OrderCompleted(body []byte) error { func (h *AmqpOrderHandler) OrderCompleted(body []byte) error {
ch, err := h.conn.Channel()
if err != nil {
return fmt.Errorf("failed to open a channel: %w", err)
}
defer ch.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
err := h.Channel.PublishWithContext(ctx, return ch.PublishWithContext(ctx,
"orders", // exchange "orders", // exchange
"new", // routing key "new", // routing key
false, // mandatory false, // mandatory
@@ -53,9 +59,5 @@ func (h *AmqpOrderHandler) OrderCompleted(body []byte) error {
ContentType: "application/json", ContentType: "application/json",
Body: body, Body: body,
}) })
if err != nil {
return fmt.Errorf("failed to publish a message: %w", err)
}
return nil
} }

View File

@@ -20,6 +20,7 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
amqp "github.com/rabbitmq/amqp091-go"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
@@ -175,6 +176,15 @@ func main() {
pool: pool, pool: pool,
} }
conn, err := amqp.Dial(amqpUrl)
if err != nil {
fmt.Errorf("failed to connect to RabbitMQ: %w", err)
}
amqpListener := actor.NewAmqpListener(conn)
amqpListener.DefineTopics()
pool.AddListener(amqpListener)
grpcSrv, err := actor.NewControlServer[*CartGrain](controlPlaneConfig, pool) grpcSrv, err := actor.NewControlServer[*CartGrain](controlPlaneConfig, pool)
if err != nil { if err != nil {
log.Fatalf("Error starting control plane gRPC server: %v\n", err) log.Fatalf("Error starting control plane gRPC server: %v\n", err)
@@ -211,12 +221,12 @@ func main() {
} }
}(GetDiscovery()) }(GetDiscovery())
orderHandler := &AmqpOrderHandler{ orderHandler := NewAmqpOrderHandler(conn)
Url: amqpUrl, orderHandler.DefineTopics()
}
klarnaClient := NewKlarnaClient(KlarnaPlaygroundUrl, os.Getenv("KLARNA_API_USERNAME"), os.Getenv("KLARNA_API_PASSWORD")) klarnaClient := NewKlarnaClient(KlarnaPlaygroundUrl, os.Getenv("KLARNA_API_USERNAME"), os.Getenv("KLARNA_API_PASSWORD"))
syncedServer := NewPoolServer(pool, fmt.Sprintf("%s, %s", name, podIp), klarnaClient) syncedServer := NewPoolServer(pool, fmt.Sprintf("%s, %s", name, podIp), klarnaClient)
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/cart/", http.StripPrefix("/cart", syncedServer.Serve())) mux.Handle("/cart/", http.StripPrefix("/cart", syncedServer.Serve()))
// only for local // only for local
@@ -447,11 +457,7 @@ func confirmOrder(order *CheckoutOrder, orderHandler *AmqpOrderHandler) error {
if err != nil { if err != nil {
return err return err
} }
err = orderHandler.Connect()
if err != nil {
return err
}
defer orderHandler.Close()
err = orderHandler.OrderCompleted(orderToSend) err = orderHandler.OrderCompleted(orderToSend)
if err != nil { if err != nil {
return err return err

View File

@@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"github.com/matst80/slask-finder/pkg/messaging"
amqp "github.com/rabbitmq/amqp091-go"
) )
type QueueEvent struct { type QueueEvent struct {
@@ -26,7 +28,47 @@ type DiskStorage[V any] struct {
type LogStorage[V any] interface { type LogStorage[V any] interface {
LoadEvents(id uint64, grain Grain[V]) error LoadEvents(id uint64, grain Grain[V]) error
AppendEvent(id uint64, msg ...proto.Message) error AppendMutations(id uint64, msg ...proto.Message) error
}
type LogListener interface {
AppendMutations(id uint64, msg ...ApplyResult)
}
type AmqpListener struct {
conn *amqp.Connection
}
func NewAmqpListener(conn *amqp.Connection) *AmqpListener {
return &AmqpListener{
conn: conn,
}
}
func (l *AmqpListener) DefineTopics() {
ch, err := l.conn.Channel()
if err != nil {
log.Fatalf("Failed to open a channel: %v", err)
}
defer ch.Close()
if err := messaging.DefineTopic(ch, "cart", "mutation"); err != nil {
log.Fatalf("Failed to declare topic mutation: %v", err)
}
}
type CartEvent struct {
Id uint64 `json:"id"`
Mutations []ApplyResult `json:"mutations"`
}
func (l *AmqpListener) AppendMutations(id uint64, msg ...ApplyResult) {
err := messaging.SendChange(l.conn, "cart", "mutation", &CartEvent{
Id: id,
Mutations: msg,
})
if err != nil {
log.Printf("Failed to send mutation event: %v", err)
}
} }
func NewDiskStorage[V any](path string, registry MutationRegistry) *DiskStorage[V] { func NewDiskStorage[V any](path string, registry MutationRegistry) *DiskStorage[V] {
@@ -108,7 +150,7 @@ func (s *DiskStorage[V]) Close() {
close(s.done) close(s.done)
} }
func (s *DiskStorage[V]) AppendEvent(id uint64, msg ...proto.Message) error { func (s *DiskStorage[V]) AppendMutations(id uint64, msg ...proto.Message) error {
if s.queue != nil { if s.queue != nil {
queue := make([]QueueEvent, 0) queue := make([]QueueEvent, 0)
data, found := s.queue.Load(id) data, found := s.queue.Load(id)

View File

@@ -16,7 +16,6 @@ import (
// It delegates to a grain pool and cluster operations to a synced pool. // It delegates to a grain pool and cluster operations to a synced pool.
type ControlServer[V any] struct { type ControlServer[V any] struct {
messages.UnimplementedControlPlaneServer messages.UnimplementedControlPlaneServer
pool GrainPool[V] pool GrainPool[V]
} }

View File

@@ -17,6 +17,7 @@ type SimpleGrainPool[V any] struct {
mutationRegistry MutationRegistry mutationRegistry MutationRegistry
spawn func(id uint64) (Grain[V], error) spawn func(id uint64) (Grain[V], error)
spawnHost func(host string) (Host, error) spawnHost func(host string) (Host, error)
listeners []LogListener
storage LogStorage[V] storage LogStorage[V]
ttl time.Duration ttl time.Duration
poolSize int poolSize int
@@ -66,6 +67,19 @@ func NewSimpleGrainPool[V any](config GrainPoolConfig[V]) (*SimpleGrainPool[V],
return p, nil return p, nil
} }
func (p *SimpleGrainPool[V]) AddListener(listener LogListener) {
p.listeners = append(p.listeners, listener)
}
func (p *SimpleGrainPool[V]) RemoveListener(listener LogListener) {
for i, l := range p.listeners {
if l == listener {
p.listeners = append(p.listeners[:i], p.listeners[i+1:]...)
break
}
}
}
func (p *SimpleGrainPool[V]) purge() { func (p *SimpleGrainPool[V]) purge() {
purgeLimit := time.Now().Add(-p.ttl) purgeLimit := time.Now().Add(-p.ttl)
purgedIds := make([]uint64, 0, len(p.grains)) purgedIds := make([]uint64, 0, len(p.grains))
@@ -383,11 +397,14 @@ func (p *SimpleGrainPool[V]) Apply(id uint64, mutation ...proto.Message) (*Mutat
} }
if p.storage != nil { if p.storage != nil {
go func() { go func() {
if err := p.storage.AppendEvent(id, mutation...); err != nil { if err := p.storage.AppendMutations(id, mutation...); err != nil {
log.Printf("failed to store mutation for grain %d: %v", id, err) log.Printf("failed to store mutation for grain %d: %v", id, err)
} }
}() }()
} }
for _, listener := range p.listeners {
go listener.AppendMutations(id, mutations...)
}
result, err := grain.GetCurrentState() result, err := grain.GetCurrentState()
if err != nil { if err != nil {
return nil, err return nil, err