192 lines
5.6 KiB
Transact-SQL
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
|