diff --git a/node-agent/WxAgent.Service/RemoteAgentHostedService.cs b/node-agent/WxAgent.Service/RemoteAgentHostedService.cs index 333f46c..21e4613 100644 --- a/node-agent/WxAgent.Service/RemoteAgentHostedService.cs +++ b/node-agent/WxAgent.Service/RemoteAgentHostedService.cs @@ -192,14 +192,36 @@ public sealed class RemoteAgentHostedService( continue; var sequence = sourceChanged ? 1 : checkpoint.ConfirmedSequence + 1; - var batch = new RemoteSyncBatch( - remote.NodeId!, account.AccountId, Guid.NewGuid().ToString("N"), collection.SourceGeneration, - "messages", sequence, SerializeCursors(cursorStart), SerializeCursors(collection.NextCursors), - string.Empty, collection.IsComplete ? "complete" : "partial", collection.Conversations, collection.Messages); - batch = batch with { PayloadHash = RemoteDataBatchAuthorization.ComputePayloadHash(batch) }; - var queued = queue.Enqueue(reporting, batch); - if (queued.Accepted && queued.Batch is not null) - state.MarkCollected(queued.Batch, collection.UnavailableSources); + var remainingConversations = collection.Conversations.ToList(); + var remainingMessages = collection.Messages.ToList(); + var cursorStartJson = SerializeCursors(cursorStart); + var cursorEndJson = SerializeCursors(collection.NextCursors); + var allQueued = true; + RemoteSyncBatch? lastQueuedBatch = null; + while (remainingConversations.Count > 0 || remainingMessages.Count > 0 || lastQueuedBatch is null) + { + var conversationCount = Math.Min(remainingConversations.Count, RemoteDataProtocol.MaxBatchMessages); + var conversations = remainingConversations.Take(conversationCount).ToArray(); + remainingConversations.RemoveRange(0, conversationCount); + var messageCount = Math.Min(remainingMessages.Count, RemoteDataProtocol.MaxBatchMessages - conversations.Length); + var messages = remainingMessages.Take(messageCount).ToArray(); + remainingMessages.RemoveRange(0, messageCount); + var batch = new RemoteSyncBatch( + remote.NodeId!, account.AccountId, Guid.NewGuid().ToString("N"), collection.SourceGeneration, + "messages", sequence++, cursorStartJson, cursorEndJson, string.Empty, + collection.IsComplete ? "complete" : "partial", conversations, messages); + batch = batch with { PayloadHash = RemoteDataBatchAuthorization.ComputePayloadHash(batch) }; + var queued = queue.Enqueue(reporting, batch); + if (!queued.Accepted || queued.Batch is null) + { + allQueued = false; + break; + } + lastQueuedBatch = queued.Batch; + cursorStartJson = cursorEndJson; + } + if (allQueued && lastQueuedBatch is not null) + state.MarkCollected(lastQueuedBatch, collection.UnavailableSources); if (!collection.IsComplete) logger.LogWarning("Data sync is partial; accountId={AccountId}; unavailableSources={UnavailableSourceCount}.", account.AccountId, collection.UnavailableSources.Count); }