Initial working version

This commit is contained in:
Lucas Rouckhout
2023-04-01 10:13:41 +02:00
parent be66edfbbe
commit df0219edda
2 changed files with 50 additions and 25 deletions

View File

@@ -161,14 +161,14 @@ func main() {
} }
logger.Infof("Connected to LND: %s - %s", getInfo.Alias, getInfo.IdentityPubkey) logger.Infof("Connected to LND: %s - %s", getInfo.Alias, getInfo.IdentityPubkey)
// If not RABBITMQ_URI was provided we will not attempt to create a client // If no RABBITMQ_URI was provided we will not attempt to create a client
// No rabbitmq features will be available in this case. // No rabbitmq features will be available in this case.
var rabbitmqClient rabbitmq.Client var rabbitmqClient rabbitmq.Client
if c.RabbitMQUri != "" { if c.RabbitMQUri != "" {
rabbitmqClient, err = rabbitmq.Dial(c.RabbitMQUri, rabbitmqClient, err = rabbitmq.Dial(c.RabbitMQUri,
rabbitmq.WithLogger(logger), rabbitmq.WithLogger(logger),
rabbitmq.WithLndInvoiceExchange(c.RabbitMQLndInvoiceExchange), rabbitmq.WithLndInvoiceExchange(c.RabbitMQLndInvoiceExchange),
rabbitmq.WithLndhubInvoiceExchange(c.RabbitMQLndhubInvoiceExchange), rabbitmq.WithLndHubInvoiceExchange(c.RabbitMQLndhubInvoiceExchange),
rabbitmq.WithLndInvoiceConsumerQueueName(c.RabbitMQInvoiceConsumerQueueName), rabbitmq.WithLndInvoiceConsumerQueueName(c.RabbitMQInvoiceConsumerQueueName),
) )
if err != nil { if err != nil {

View File

@@ -16,6 +16,11 @@ import (
"sync" "sync"
) )
// bufPool is a classic buffer pool pattern that allows more clever reuse of heap memory.
// Instead of allocating new memory everytime we need to encode the invoices we
// reuse buffers from this buffer pool. If we consume events sequentially there will
// only be one buffer in this pool at all times, but when scaling to multiple go
// routines this memory pool will scale with it.
var bufPool = sync.Pool{ var bufPool = sync.Pool{
New: func() interface{} { return new(bytes.Buffer) }, New: func() interface{} { return new(bytes.Buffer) },
} }
@@ -24,9 +29,18 @@ const (
contentTypeJSON = "application/json" contentTypeJSON = "application/json"
) )
type (
InvoiceHandler = func(ctx context.Context, invoice *lnrpc.Invoice) error
SubscribeToInvoicesFunc = func() (in chan models.Invoice, out chan models.Invoice, err error)
EncodeWebhookInvoicePayloadFunc = func(ctx context.Context, w io.Writer, invoice models.Invoice) error
)
// InvoiceHandler is a closure that defined what to do with an invoice once it's been consumed from the rabbitmq topic
type Client interface { type Client interface {
SubscribeToLndInvoices(context.Context, InvoiceHandler) error SubscribeToLndInvoices(context.Context, InvoiceHandler) error
StartPublishInvoices(context.Context, SubscribeToInvoicesFunc, EncodeWebhookInvoicePayloadFunc) error StartPublishInvoices(context.Context, SubscribeToInvoicesFunc, EncodeWebhookInvoicePayloadFunc) error
// Close will close all connections to rabbitmq
Close() error Close() error
} }
@@ -43,7 +57,7 @@ type DefaultClient struct {
lndInvoiceConsumerQueueName string lndInvoiceConsumerQueueName string
lndInvoiceExchange string lndInvoiceExchange string
lndhubInvoiceExchange string lndHubInvoiceExchange string
} }
type ClientOption = func(client *DefaultClient) type ClientOption = func(client *DefaultClient)
@@ -54,9 +68,9 @@ func WithLndInvoiceExchange(exchange string) ClientOption {
} }
} }
func WithLndhubInvoiceExchange(exchange string) ClientOption { func WithLndHubInvoiceExchange(exchange string) ClientOption {
return func(client *DefaultClient) { return func(client *DefaultClient) {
client.lndhubInvoiceExchange = exchange client.lndHubInvoiceExchange = exchange
} }
} }
@@ -72,6 +86,7 @@ func WithLogger(logger *lecho.Logger) ClientOption {
} }
} }
// Dial sets up a connection to rabbitmq with two channels that are ready to produce and consume
func Dial(uri string, options ...ClientOption) (Client, error) { func Dial(uri string, options ...ClientOption) (Client, error) {
conn, err := amqp.Dial(uri) conn, err := amqp.Dial(uri)
if err != nil { if err != nil {
@@ -102,7 +117,7 @@ func Dial(uri string, options ...ClientOption) (Client, error) {
lndInvoiceConsumerQueueName: "lndhub_invoice_consumer", lndInvoiceConsumerQueueName: "lndhub_invoice_consumer",
lndInvoiceExchange: "lnd_invoice", lndInvoiceExchange: "lnd_invoice",
lndhubInvoiceExchange: "lndhub_invoice", lndHubInvoiceExchange: "lndhub_invoice",
} }
for _, opt := range options { for _, opt := range options {
@@ -114,8 +129,6 @@ func Dial(uri string, options ...ClientOption) (Client, error) {
func (client *DefaultClient) Close() error { return client.conn.Close() } func (client *DefaultClient) Close() error { return client.conn.Close() }
type InvoiceHandler = func(ctx context.Context, invoice *lnrpc.Invoice) error
func (client *DefaultClient) SubscribeToLndInvoices(ctx context.Context, handler InvoiceHandler) error { func (client *DefaultClient) SubscribeToLndInvoices(ctx context.Context, handler InvoiceHandler) error {
queue, err := client.consumeChannel.QueueDeclare( queue, err := client.consumeChannel.QueueDeclare(
client.lndInvoiceConsumerQueueName, client.lndInvoiceConsumerQueueName,
@@ -171,13 +184,14 @@ func (client *DefaultClient) SubscribeToLndInvoices(ctx context.Context, handler
err := json.Unmarshal(delivery.Body, &invoice) err := json.Unmarshal(delivery.Body, &invoice)
if err != nil { if err != nil {
client.logger.Error(err) captureErr(client.logger, err)
sentry.CaptureException(err)
// If we can't even Unmarshall the message we are dealing with
// badly formatted events. In that case we simply Nack the message
// and explicitly do not requeue it.
err = delivery.Nack(false, false) err = delivery.Nack(false, false)
if err != nil { if err != nil {
client.logger.Error(err) captureErr(client.logger, err)
sentry.CaptureException(err)
} }
continue continue
@@ -185,21 +199,27 @@ func (client *DefaultClient) SubscribeToLndInvoices(ctx context.Context, handler
err = handler(ctx, &invoice) err = handler(ctx, &invoice)
if err != nil { if err != nil {
client.logger.Error(err) captureErr(client.logger, err)
sentry.CaptureException(err)
// If for some reason we can't handle the message we instruct rabbitmq to
// requeue the message in hopes of finding another consumer that can deal
// with this message.
err := delivery.Nack(false, true)
if err != nil {
captureErr(client.logger, err)
}
delivery.Nack(false, false)
continue continue
} }
delivery.Ack(false) err = delivery.Ack(false)
if err != nil {
captureErr(client.logger, err)
}
} }
} }
} }
type SubscribeToInvoicesFunc = func() (in chan models.Invoice, out chan models.Invoice, err error)
type EncodeWebhookInvoicePayloadFunc = func(ctx context.Context, w io.Writer, invoice models.Invoice) error
func (client *DefaultClient) StartPublishInvoices(ctx context.Context, invoicesSubscribeFunc SubscribeToInvoicesFunc, payloadFunc EncodeWebhookInvoicePayloadFunc) error { func (client *DefaultClient) StartPublishInvoices(ctx context.Context, invoicesSubscribeFunc SubscribeToInvoicesFunc, payloadFunc EncodeWebhookInvoicePayloadFunc) error {
err := client.publishChannel.ExchangeDeclare( err := client.publishChannel.ExchangeDeclare(
client.lndInvoiceExchange, client.lndInvoiceExchange,
@@ -212,7 +232,7 @@ func (client *DefaultClient) StartPublishInvoices(ctx context.Context, invoicesS
// Non-Internal exchange's accept direct publishing // Non-Internal exchange's accept direct publishing
false, false,
// Nowait: We set this to false as we want to wait for a server response // Nowait: We set this to false as we want to wait for a server response
// to check wether the exchange was created succesfully // to check whether the exchange was created succesfully
false, false,
nil, nil,
) )
@@ -233,15 +253,15 @@ func (client *DefaultClient) StartPublishInvoices(ctx context.Context, invoicesS
return context.Canceled return context.Canceled
case incomingInvoice := <-in: case incomingInvoice := <-in:
err = client.publishToLndhubExchange(ctx, incomingInvoice, payloadFunc) err = client.publishToLndhubExchange(ctx, incomingInvoice, payloadFunc)
if err != nil { if err != nil {
client.logger.Error(err) captureErr(client.logger, err)
sentry.CaptureException(err)
} }
case outgoing := <-out: case outgoing := <-out:
err = client.publishToLndhubExchange(ctx, outgoing, payloadFunc) err = client.publishToLndhubExchange(ctx, outgoing, payloadFunc)
if err != nil { if err != nil {
client.logger.Error(err) captureErr(client.logger, err)
sentry.CaptureException(err)
} }
} }
} }
@@ -257,7 +277,7 @@ func (client *DefaultClient) publishToLndhubExchange(ctx context.Context, invoic
key := fmt.Sprintf("invoice.%s.%s", invoice.Type, invoice.State) key := fmt.Sprintf("invoice.%s.%s", invoice.Type, invoice.State)
err = client.publishChannel.PublishWithContext(ctx, err = client.publishChannel.PublishWithContext(ctx,
client.lndhubInvoiceExchange, client.lndHubInvoiceExchange,
key, key,
false, false,
false, false,
@@ -274,3 +294,8 @@ func (client *DefaultClient) publishToLndhubExchange(ctx context.Context, invoic
return nil return nil
} }
func captureErr(logger *lecho.Logger, err error) {
logger.Error(err)
sentry.CaptureException(err)
}