fix(shared): filter due proposals by source_platform to prevent cross-platform race
PR Checks / test-and-build (pull_request) Successful in 6m11s
PR Checks / test-and-build (pull_request) Successful in 6m11s
Both Telegram and Discord deadline services were querying ALL due proposals without filtering by source_platform. If the Telegram service reached a Discord proposal first, it finalized the DB state but skipped message handling. The Discord service then saw status != 'Voting' and never updated the Discord vote message. Fix: GetDueProposalIdsAsync now accepts a sourcePlatform parameter and filters at the DB level. Each service only processes its own platform's proposals. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -46,7 +46,7 @@ public sealed class RescheduleVotingDeadlineService(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var proposalIds = await finalizer.GetDueProposalIdsAsync(ct);
|
var proposalIds = await finalizer.GetDueProposalIdsAsync("Telegram", ct);
|
||||||
|
|
||||||
foreach (var proposalId in proposalIds)
|
foreach (var proposalId in proposalIds)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public sealed class DiscordRescheduleVotingDeadlineService(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var proposalIds = await finalizer.GetDueProposalIdsAsync(ct);
|
var proposalIds = await finalizer.GetDueProposalIdsAsync("Discord", ct);
|
||||||
foreach (var id in proposalIds)
|
foreach (var id in proposalIds)
|
||||||
{
|
{
|
||||||
await TryFinalizeAsync(id, ct);
|
await TryFinalizeAsync(id, ct);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public sealed class RescheduleVotingFinalizer(
|
|||||||
ISystemClock clock,
|
ISystemClock clock,
|
||||||
ILogger<RescheduleVotingFinalizer> logger)
|
ILogger<RescheduleVotingFinalizer> logger)
|
||||||
{
|
{
|
||||||
public async Task<IReadOnlyList<Guid>> GetDueProposalIdsAsync(CancellationToken ct)
|
public async Task<IReadOnlyList<Guid>> GetDueProposalIdsAsync(string sourcePlatform, CancellationToken ct)
|
||||||
{
|
{
|
||||||
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
||||||
var proposalIds = (await connection.QueryAsync<Guid>(
|
var proposalIds = (await connection.QueryAsync<Guid>(
|
||||||
@@ -36,10 +36,11 @@ public sealed class RescheduleVotingFinalizer(
|
|||||||
WHERE status = 'Voting'
|
WHERE status = 'Voting'
|
||||||
AND voting_deadline_at IS NOT NULL
|
AND voting_deadline_at IS NOT NULL
|
||||||
AND voting_deadline_at <= @Now
|
AND voting_deadline_at <= @Now
|
||||||
|
AND source_platform = @SourcePlatform
|
||||||
ORDER BY voting_deadline_at
|
ORDER BY voting_deadline_at
|
||||||
LIMIT 25
|
LIMIT 25
|
||||||
""",
|
""",
|
||||||
new { Now = clock.UtcNow.UtcDateTime })).ToList();
|
new { Now = clock.UtcNow.UtcDateTime, SourcePlatform = sourcePlatform })).ToList();
|
||||||
|
|
||||||
return proposalIds;
|
return proposalIds;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user