C#: receipt server follows Heimdall's review of #61
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 5m30s
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Successful in 21s
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 5m30s
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Successful in 21s
Look the id up first and authorize only on first sighting; remember refusals within the cap; never re-forward an id whose primary stayed silent; end the connection when an answer cannot be queued; fixed refusal texts; limit_reached cap refusal; commit-point doc on ApplyPut/Delete. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b0ea18371f
commit
ed5f9d30d1
6 changed files with 362 additions and 118 deletions
|
|
@ -345,7 +345,7 @@ namespace GameCult.Networking
|
|||
}
|
||||
|
||||
// The write's receipt state, or null when its connection did not opt in (it is then answered exactly as before).
|
||||
private static CultNetReceiptedWrite? BeginReceipted(
|
||||
private CultNetReceiptedWrite? BeginReceipted(
|
||||
CultNetServerPeer peer,
|
||||
string operation,
|
||||
string? messageId,
|
||||
|
|
@ -362,8 +362,22 @@ namespace GameCult.Networking
|
|||
: new CultNetReceiptedWrite(session, message => Answer(peer, message), operation, messageId, schemaId, recordKey, payload);
|
||||
}
|
||||
|
||||
// Every frame a write can be answered with. Mock point for tests: the answer leaves through the peer's transport.
|
||||
internal static void Answer(CultNetServerPeer peer, ICultNetSchemaMessage message)
|
||||
// Every frame a write can be answered with. Mock point for tests: the answer leaves through the peer's transport. An
|
||||
// answer that cannot be queued ends the connection: the peer settles its write as session_ended, never as silence.
|
||||
private void Answer(CultNetServerPeer peer, ICultNetSchemaMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
Send(peer, message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_server.Logger.LogWarning($"CultNet write answer could not be sent; ending the connection: {ex.Message}");
|
||||
peer.Transport?.End();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Send(CultNetServerPeer peer, ICultNetSchemaMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
|
|
@ -393,7 +407,10 @@ namespace GameCult.Networking
|
|||
}
|
||||
|
||||
_server.Logger.LogWarning($"CultNet forwarded write was not answered by its primary: {failure.Message}");
|
||||
write.Unanswered();
|
||||
// Silence past the window means the primary may have applied it: never forward the id again on this session.
|
||||
// Any other failure never reached a primary that could have.
|
||||
if (failure is CultNetWriteUnconfirmedException) write.LeftSilent();
|
||||
else write.Unanswered();
|
||||
}
|
||||
|
||||
private async Task HandlePutAsync(CultNetDocumentPutRawMessage message, CultNetServerPeer peer)
|
||||
|
|
@ -405,16 +422,14 @@ namespace GameCult.Networking
|
|||
return;
|
||||
}
|
||||
|
||||
if (write != null && !write.Begin()) return;
|
||||
|
||||
var authorize = Authorize(peer, CultNetRemoteWriteOperation.Put, message.ShardId, message.ShardEpoch);
|
||||
try
|
||||
{
|
||||
await _database.ApplyPutAsync(message, write == null ? authorize : write.Guard(authorize)).ConfigureAwait(false);
|
||||
await _database.ApplyPutAsync(message, authorize).ConfigureAwait(false);
|
||||
write?.Admitted();
|
||||
}
|
||||
catch (CultNetWriteResendException resend) when (write != null)
|
||||
{
|
||||
write.Resent(resend);
|
||||
}
|
||||
catch (CultNetRemoteWriteRefusedException ex)
|
||||
{
|
||||
_server.Logger.LogWarning($"CultNet raw put refused: {ex.Message}");
|
||||
|
|
@ -462,7 +477,7 @@ namespace GameCult.Networking
|
|||
catch (Exception ex)
|
||||
{
|
||||
_server.Logger.LogError($"CultNet raw put failed: {ex.Message}");
|
||||
Refuse(peer, write, new CultNetErrorMessage { Error = ex.Message });
|
||||
Refuse(peer, write, new CultNetErrorMessage { Error = CultNetWriteReceiptSession.WriteFailedText });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,16 +490,14 @@ namespace GameCult.Networking
|
|||
return;
|
||||
}
|
||||
|
||||
if (write != null && !write.Begin()) return;
|
||||
|
||||
var authorize = Authorize(peer, CultNetRemoteWriteOperation.Delete, message.ShardId, message.ShardEpoch);
|
||||
try
|
||||
{
|
||||
await _database.ApplyDeleteAsync(message, write == null ? authorize : write.Guard(authorize)).ConfigureAwait(false);
|
||||
await _database.ApplyDeleteAsync(message, authorize).ConfigureAwait(false);
|
||||
write?.Admitted();
|
||||
}
|
||||
catch (CultNetWriteResendException resend) when (write != null)
|
||||
{
|
||||
write.Resent(resend);
|
||||
}
|
||||
catch (CultNetRemoteWriteRefusedException ex)
|
||||
{
|
||||
_server.Logger.LogWarning($"CultNet raw delete refused: {ex.Message}");
|
||||
|
|
@ -528,7 +541,7 @@ namespace GameCult.Networking
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Refuse(peer, write, new CultNetErrorMessage { Error = ex.Message });
|
||||
Refuse(peer, write, new CultNetErrorMessage { Error = CultNetWriteReceiptSession.WriteFailedText });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue