Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 a56567449a Remove legacy polling wait and add exponential backoff to reconnect
Changes:
- Remove 5-second wait in GetUpdates that was used for long-polling.
  With streaming, WaitForUpdatesAndPush already waits for updates before
  calling GetUpdates, making this redundant. GetGameStatus is deprecated.
- Add exponential backoff to Eagle's stream reconnection logic, starting
  at 1 second and doubling up to 30 seconds max.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 22:54:28 -08:00
2 changed files with 13 additions and 15 deletions
@@ -24,9 +24,6 @@ using std::scoped_lock;
using std::string;
using std::unique_lock;
using std::weak_ptr;
using std::chrono::duration;
static constexpr duration kWaitForUpdatesDuration = std::chrono::milliseconds(5000);
using net::eagle0::shardok::common::GameStatus;
@@ -278,15 +275,9 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
awrs = engine->GetGameHistory(startingActionId);
incomingRegistrations--;
// If the current player is an AI, wait for some results to post. Otherwise go ahead and
// return, we might be telling the caller about available commands.
if (awrs.empty() && !engine->GameIsOver() &&
engine->GetPlayerInfos()[engine->GetCurrentPlayerId()].is_ai()) {
updateCondition.wait_for(guard, kWaitForUpdatesDuration);
incomingRegistrations++;
awrs = engine->GetGameHistory(startingActionId);
incomingRegistrations--;
}
// Note: Previously this had a 5-second wait for AI players to support long-polling.
// With streaming (WaitForUpdatesAndPush), the caller already waits for updates,
// so this wait is no longer needed. GetGameStatus is deprecated in favor of streaming.
updates.mainResults.reserve(awrs.size());
std::ranges::transform(
@@ -123,7 +123,7 @@ class ShardokInterfaceGrpcClient(
grpcClient.subscribeToGame(request, responseObserver)
}
/** Schedules a reconnection attempt after stream disconnection. */
/** Schedules a reconnection attempt after stream disconnection with exponential backoff. */
private def scheduleReconnect(
eagleGameId: GameId,
shardokGameId: ShardokGameId,
@@ -133,8 +133,15 @@ class ShardokInterfaceGrpcClient(
Thread.sleep(delayMs)
pendingBattles.synchronized {
if pendingBattles.contains(shardokGameId) then
println(s"Reconnecting stream for $shardokGameId")
subscribeToGame(eagleGameId, shardokGameId, None)
println(s"Reconnecting stream for $shardokGameId (delay was ${delayMs}ms)")
try subscribeToGame(eagleGameId, shardokGameId, None)
catch {
case e: Exception =>
println(s"Reconnect failed for $shardokGameId: $e")
// Exponential backoff with max 30 seconds
val nextDelay = Math.min(delayMs * 2, 30000)
scheduleReconnect(eagleGameId, shardokGameId, nextDelay)
}
}
}