[hw-6] add notifier service, kafka

This commit is contained in:
Никита Шубин
2025-07-17 19:20:27 +00:00
parent 424d6905da
commit 6e1ad86128
33 changed files with 1412 additions and 92 deletions

View File

@@ -0,0 +1,11 @@
package outbox
import "route256/loms/internal/domain/entity"
type Event struct {
ID int64
OrderID entity.ID
Topic string
Key string
Payload []byte
}

View File

@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@@ -0,0 +1,5 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc

View File

@@ -0,0 +1,18 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
package sqlc
import (
"context"
)
type Querier interface {
OutboxInsert(ctx context.Context, arg *OutboxInsertParams) error
OutboxMarkError(ctx context.Context, dollar_1 []int64) (int64, error)
OutboxMarkSent(ctx context.Context, dollar_1 []int64) (int64, error)
OutboxSelectForPublish(ctx context.Context, limit int32) ([]*OutboxSelectForPublishRow, error)
}
var _ Querier = (*Queries)(nil)

View File

@@ -0,0 +1,22 @@
-- name: OutboxInsert :exec
INSERT INTO outbox (order_id, topic, "key", payload)
VALUES ($1, $2, $3, $4::jsonb);
-- name: OutboxSelectForPublish :many
SELECT id, order_id, topic, "key", payload
FROM outbox
WHERE status = 'new'
ORDER BY created_at
LIMIT $1
FOR UPDATE SKIP LOCKED;
-- name: OutboxMarkSent :execrows
UPDATE outbox
SET status = 'sent',
sent_at = now()
WHERE id = ANY($1::bigint[]);
-- name: OutboxMarkError :execrows
UPDATE outbox
SET status = 'error'
WHERE id = ANY($1::bigint[]);

View File

@@ -0,0 +1,104 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: query.sql
package sqlc
import (
"context"
)
const outboxInsert = `-- name: OutboxInsert :exec
INSERT INTO outbox (order_id, topic, "key", payload)
VALUES ($1, $2, $3, $4::jsonb)
`
type OutboxInsertParams struct {
OrderID int64
Topic string
Key *string
Column4 []byte
}
func (q *Queries) OutboxInsert(ctx context.Context, arg *OutboxInsertParams) error {
_, err := q.db.Exec(ctx, outboxInsert,
arg.OrderID,
arg.Topic,
arg.Key,
arg.Column4,
)
return err
}
const outboxMarkError = `-- name: OutboxMarkError :execrows
UPDATE outbox
SET status = 'error'
WHERE id = ANY($1::bigint[])
`
func (q *Queries) OutboxMarkError(ctx context.Context, dollar_1 []int64) (int64, error) {
result, err := q.db.Exec(ctx, outboxMarkError, dollar_1)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const outboxMarkSent = `-- name: OutboxMarkSent :execrows
UPDATE outbox
SET status = 'sent',
sent_at = now()
WHERE id = ANY($1::bigint[])
`
func (q *Queries) OutboxMarkSent(ctx context.Context, dollar_1 []int64) (int64, error) {
result, err := q.db.Exec(ctx, outboxMarkSent, dollar_1)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const outboxSelectForPublish = `-- name: OutboxSelectForPublish :many
SELECT id, order_id, topic, "key", payload
FROM outbox
WHERE status = 'new'
ORDER BY created_at
LIMIT $1
FOR UPDATE SKIP LOCKED
`
type OutboxSelectForPublishRow struct {
ID int64
OrderID int64
Topic string
Key *string
Payload []byte
}
func (q *Queries) OutboxSelectForPublish(ctx context.Context, limit int32) ([]*OutboxSelectForPublishRow, error) {
rows, err := q.db.Query(ctx, outboxSelectForPublish, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []*OutboxSelectForPublishRow
for rows.Next() {
var i OutboxSelectForPublishRow
if err := rows.Scan(
&i.ID,
&i.OrderID,
&i.Topic,
&i.Key,
&i.Payload,
); err != nil {
return nil, err
}
items = append(items, &i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@@ -0,0 +1,85 @@
package sqlc
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"route256/loms/internal/domain/entity"
"route256/loms/internal/domain/repository/outbox"
"route256/loms/internal/infra/postgres"
)
type outboxRepo struct {
db *pgxpool.Pool
}
func NewOutboxRepository(masterPool *pgxpool.Pool) *outboxRepo {
return &outboxRepo{
db: masterPool,
}
}
func (r *outboxRepo) GetQuerier(ctx context.Context) *Queries {
tx, ok := postgres.TxFromCtx(ctx)
if ok {
return New(tx)
}
return New(r.db)
}
func (r *outboxRepo) AddEvent(ctx context.Context, evt outbox.Event) error {
querier := r.GetQuerier(ctx)
return querier.OutboxInsert(ctx, &OutboxInsertParams{
OrderID: int64(evt.OrderID),
Topic: evt.Topic,
Key: &evt.Key,
Column4: evt.Payload,
})
}
func (r *outboxRepo) WithNewEvents(ctx context.Context, limit int32, handler func(context.Context, outbox.Event) error) error {
querier := r.GetQuerier(ctx)
rows, err := querier.OutboxSelectForPublish(ctx, int32(limit))
if err != nil {
return err
}
if len(rows) == 0 {
return nil
}
var sentIDs, errIDs []int64
for _, row := range rows {
ev := outbox.Event{
ID: row.ID,
OrderID: entity.ID(row.OrderID),
Topic: row.Topic,
Key: *row.Key,
Payload: row.Payload,
}
if err := handler(ctx, ev); err != nil {
errIDs = append(errIDs, row.ID)
} else {
sentIDs = append(sentIDs, row.ID)
}
}
if len(sentIDs) > 0 {
if _, err := querier.OutboxMarkSent(ctx, sentIDs); err != nil {
return err
}
}
if len(errIDs) > 0 {
if _, err := querier.OutboxMarkError(ctx, errIDs); err != nil {
return err
}
}
return nil
}