feat: use postgres
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package mailstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
pgtable "galaxy/mail/internal/adapters/postgres/jet/mail/table"
|
||||
"galaxy/mail/internal/service/renderdelivery"
|
||||
|
||||
pg "github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
// RenderDelivery returns a handle that satisfies renderdelivery.Store.
|
||||
func (store *Store) RenderDelivery() *RenderDeliveryStore {
|
||||
return &RenderDeliveryStore{store: store}
|
||||
}
|
||||
|
||||
// RenderDeliveryStore is the renderdelivery.Store handle returned by
|
||||
// Store.RenderDelivery.
|
||||
type RenderDeliveryStore struct {
|
||||
store *Store
|
||||
}
|
||||
|
||||
var _ renderdelivery.Store = (*RenderDeliveryStore)(nil)
|
||||
|
||||
// MarkRendered persists the rendered subject, bodies, and locale_fallback
|
||||
// flag for a queued template-mode delivery and transitions its status to
|
||||
// rendered. The active attempt remains scheduled with its existing
|
||||
// scheduled_for so the scheduler picks the row up via next_attempt_at.
|
||||
func (handle *RenderDeliveryStore) MarkRendered(ctx context.Context, input renderdelivery.MarkRenderedInput) error {
|
||||
if handle == nil || handle.store == nil {
|
||||
return errors.New("mark rendered: nil store")
|
||||
}
|
||||
if ctx == nil {
|
||||
return errors.New("mark rendered: nil context")
|
||||
}
|
||||
if err := input.Validate(); err != nil {
|
||||
return fmt.Errorf("mark rendered: %w", err)
|
||||
}
|
||||
|
||||
return handle.store.withTx(ctx, "mark rendered", func(ctx context.Context, tx *sql.Tx) error {
|
||||
// Lock the active attempt for the duration of the update so a
|
||||
// concurrent attempt-claim races against the same row.
|
||||
lockStmt := pg.SELECT(pgtable.Attempts.ScheduledFor).
|
||||
FROM(pgtable.Attempts).
|
||||
WHERE(pg.AND(
|
||||
pgtable.Attempts.DeliveryID.EQ(pg.String(input.Delivery.DeliveryID.String())),
|
||||
pgtable.Attempts.AttemptNo.EQ(pg.Int(int64(input.Delivery.AttemptCount))),
|
||||
)).
|
||||
FOR(pg.UPDATE())
|
||||
|
||||
lockQuery, lockArgs := lockStmt.Sql()
|
||||
row := tx.QueryRowContext(ctx, lockQuery, lockArgs...)
|
||||
var ignored any
|
||||
if err := row.Scan(&ignored); err != nil {
|
||||
return fmt.Errorf("mark rendered: lock active attempt: %w", err)
|
||||
}
|
||||
if err := lockDelivery(ctx, tx, input.Delivery.DeliveryID); err != nil {
|
||||
return fmt.Errorf("mark rendered: %w", err)
|
||||
}
|
||||
|
||||
activeAttempt, err := loadActiveAttempt(ctx, tx, input.Delivery.DeliveryID, input.Delivery.AttemptCount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mark rendered: load active attempt: %w", err)
|
||||
}
|
||||
if err := updateDelivery(ctx, tx, input.Delivery, &activeAttempt); err != nil {
|
||||
return fmt.Errorf("mark rendered: update delivery: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// MarkRenderFailed persists one classified terminal render failure. The
|
||||
// active attempt becomes terminal (`render_failed`) and the delivery becomes
|
||||
// `failed`.
|
||||
func (handle *RenderDeliveryStore) MarkRenderFailed(ctx context.Context, input renderdelivery.MarkRenderFailedInput) error {
|
||||
if handle == nil || handle.store == nil {
|
||||
return errors.New("mark render failed: nil store")
|
||||
}
|
||||
if ctx == nil {
|
||||
return errors.New("mark render failed: nil context")
|
||||
}
|
||||
if err := input.Validate(); err != nil {
|
||||
return fmt.Errorf("mark render failed: %w", err)
|
||||
}
|
||||
|
||||
return handle.store.withTx(ctx, "mark render failed", func(ctx context.Context, tx *sql.Tx) error {
|
||||
if err := lockDelivery(ctx, tx, input.Delivery.DeliveryID); err != nil {
|
||||
return fmt.Errorf("mark render failed: %w", err)
|
||||
}
|
||||
if err := updateAttempt(ctx, tx, input.Attempt); err != nil {
|
||||
return fmt.Errorf("mark render failed: update attempt: %w", err)
|
||||
}
|
||||
if err := updateDelivery(ctx, tx, input.Delivery, nil); err != nil {
|
||||
return fmt.Errorf("mark render failed: update delivery: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user