PlotDirector/PlotLine/Sql/039_Phase4D_EmailQueue.sql
2026-06-06 19:46:02 +01:00

192 lines
5.6 KiB
Transact-SQL

SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF OBJECT_ID(N'dbo.EmailQueue', N'U') IS NULL
BEGIN
CREATE TABLE dbo.EmailQueue
(
EmailQueueID bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_EmailQueue PRIMARY KEY,
ToEmail nvarchar(256) NOT NULL,
ToName nvarchar(200) NULL,
FromEmail nvarchar(256) NOT NULL,
FromName nvarchar(200) NULL,
Subject nvarchar(300) NOT NULL,
PlainTextBody nvarchar(max) NOT NULL,
HtmlBody nvarchar(max) NULL,
Status nvarchar(30) NOT NULL CONSTRAINT DF_EmailQueue_Status DEFAULT N'Pending',
AttemptCount int NOT NULL CONSTRAINT DF_EmailQueue_AttemptCount DEFAULT 0,
MaxAttempts int NOT NULL CONSTRAINT DF_EmailQueue_MaxAttempts DEFAULT 5,
NextAttemptUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_NextAttemptUtc DEFAULT SYSUTCDATETIME(),
LastAttemptUtc datetime2 NULL,
SentUtc datetime2 NULL,
LastError nvarchar(max) NULL,
CreatedUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_CreatedUtc DEFAULT SYSUTCDATETIME(),
UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_UpdatedUtc DEFAULT SYSUTCDATETIME(),
CONSTRAINT CK_EmailQueue_Status CHECK (Status IN (N'Pending', N'Sending', N'Sent', N'Failed'))
);
END;
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_EmailQueue_StatusNextAttempt' AND object_id = OBJECT_ID(N'dbo.EmailQueue'))
CREATE INDEX IX_EmailQueue_StatusNextAttempt ON dbo.EmailQueue(Status, NextAttemptUtc) INCLUDE (AttemptCount, MaxAttempts, CreatedUtc);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_EmailQueue_CreatedUtc' AND object_id = OBJECT_ID(N'dbo.EmailQueue'))
CREATE INDEX IX_EmailQueue_CreatedUtc ON dbo.EmailQueue(CreatedUtc);
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_Create
@ToEmail nvarchar(256),
@ToName nvarchar(200) = NULL,
@FromEmail nvarchar(256),
@FromName nvarchar(200) = NULL,
@Subject nvarchar(300),
@PlainTextBody nvarchar(max),
@HtmlBody nvarchar(max) = NULL,
@MaxAttempts int = 5
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.EmailQueue
(
ToEmail,
ToName,
FromEmail,
FromName,
Subject,
PlainTextBody,
HtmlBody,
Status,
AttemptCount,
MaxAttempts,
NextAttemptUtc,
CreatedUtc,
UpdatedUtc
)
VALUES
(
@ToEmail,
@ToName,
@FromEmail,
@FromName,
@Subject,
@PlainTextBody,
@HtmlBody,
N'Pending',
0,
@MaxAttempts,
SYSUTCDATETIME(),
SYSUTCDATETIME(),
SYSUTCDATETIME()
);
SELECT CAST(SCOPE_IDENTITY() AS bigint);
END;
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_DequeueBatch
@BatchSize int = 10
AS
BEGIN
SET NOCOUNT ON;
IF @BatchSize IS NULL OR @BatchSize <= 0
SET @BatchSize = 10;
;WITH NextEmails AS
(
SELECT TOP (@BatchSize) *
FROM dbo.EmailQueue WITH (UPDLOCK, READPAST, ROWLOCK)
WHERE Status = N'Pending'
AND NextAttemptUtc <= SYSUTCDATETIME()
AND AttemptCount < MaxAttempts
ORDER BY CreatedUtc, EmailQueueID
)
UPDATE NextEmails
SET Status = N'Sending',
LastAttemptUtc = SYSUTCDATETIME(),
UpdatedUtc = SYSUTCDATETIME()
OUTPUT inserted.EmailQueueID, inserted.ToEmail, inserted.ToName, inserted.FromEmail, inserted.FromName,
inserted.Subject, inserted.PlainTextBody, inserted.HtmlBody, inserted.Status, inserted.AttemptCount,
inserted.MaxAttempts, inserted.NextAttemptUtc, inserted.LastAttemptUtc, inserted.SentUtc,
inserted.LastError, inserted.CreatedUtc, inserted.UpdatedUtc;
END;
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkSending
@EmailQueueID bigint
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.EmailQueue
SET Status = N'Sending',
LastAttemptUtc = SYSUTCDATETIME(),
UpdatedUtc = SYSUTCDATETIME()
WHERE EmailQueueID = @EmailQueueID
AND Status = N'Pending'
AND AttemptCount < MaxAttempts;
END;
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkSent
@EmailQueueID bigint
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.EmailQueue
SET Status = N'Sent',
SentUtc = SYSUTCDATETIME(),
LastError = NULL,
UpdatedUtc = SYSUTCDATETIME()
WHERE EmailQueueID = @EmailQueueID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkFailed
@EmailQueueID bigint,
@LastError nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Now datetime2 = SYSUTCDATETIME();
UPDATE dbo.EmailQueue
SET AttemptCount = AttemptCount + 1,
LastAttemptUtc = @Now,
LastError = @LastError,
Status = CASE WHEN AttemptCount + 1 >= MaxAttempts THEN N'Failed' ELSE N'Pending' END,
NextAttemptUtc = CASE
WHEN AttemptCount + 1 >= MaxAttempts THEN NextAttemptUtc
WHEN AttemptCount + 1 = 1 THEN DATEADD(minute, 1, @Now)
WHEN AttemptCount + 1 = 2 THEN DATEADD(minute, 5, @Now)
ELSE DATEADD(minute, 15, @Now)
END,
UpdatedUtc = @Now
WHERE EmailQueueID = @EmailQueueID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.EmailQueue_ResetStuckSending
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Now datetime2 = SYSUTCDATETIME();
DECLARE @Cutoff datetime2 = DATEADD(minute, -10, @Now);
UPDATE dbo.EmailQueue
SET Status = CASE WHEN AttemptCount >= MaxAttempts THEN N'Failed' ELSE N'Pending' END,
NextAttemptUtc = CASE WHEN AttemptCount >= MaxAttempts THEN NextAttemptUtc ELSE @Now END,
UpdatedUtc = @Now
WHERE Status = N'Sending'
AND LastAttemptUtc IS NOT NULL
AND LastAttemptUtc < @Cutoff;
END;
GO