pypoker
pypoker copied to clipboard
[Feature request] Avoid H15 errors by keeping connection open
I am keen to use this nice web-app for a Christmas game with friends, but would also like to remove the bet timeouts, so that people aren't rushed.
I've extended the timeout, but now get h15 errors:
at=error code=H15 desc="Idle connection" method=GET path="/poker/texas-holdem"
due to the heroku 55 second timeout window ( see https://devcenter.heroku.com/articles/http-routing#timeouts)
Would it be possible to include a feature to ping the connection and keep it open if the bet-timeout is extended beyond the 55 heroku timeout limit?
if I remember correctly there's already a ping kind of message available, so I'd imagine this wouldn't be too hard to do. it'd be nice if timeouts were somehow configurable via an environment variable. I don't have time to work on this unfortunately, but feel free to send a PR
Thanks for this @epifab
I had a look through and found the ping message already in there, and had a go at implementing something to prevent the timeout. I'm new to web-apps, and have never played about with them until now. This is a bit hacky, but I wanted to see if it works.
I wrapped the bet_round
in poker_game.py
, and included a call to ping each of the players every 40 seconds:
def bet_round(self, dealer_id: str, bets: Dict[str, float], get_bet_function, on_bet_function=None) -> Optional[PlayerServer]:
round_queue = Queue()
got_best = threading.Event()
on_bet_function = None
round_thread = threading.Thread(target=lambda q, args: q.put(self.bet_round_wrapped(*args)), args=[round_queue,[dealer_id, bets, get_bet_function, got_best, on_bet_function]])
round_thread.start()
while not got_best.wait(timeout=40):
players = list(self._game_players.round(dealer_id))
print("list of players",players,type(players[0]))
if players:
player_list = list(self._game_players.round(dealer_id))
for player in player_list:
print("in bet_round - gonna ping player",player)
if not player.ping():
print("Unable to ping, gonna remove player")
self._game_players.remove(player.id)
else:
print("successfully pinged player")
else:
print("all out of players")
got_best.set()
print("Left the loop here")
round_thread.join()
best_player = round_thread.join()
return best_player
def bet_round_wrapped(self, dealer_id: str, bets: Dict[str, float], get_bet_function, got_best, on_bet_function=None) -> Optional[PlayerServer]:
"""
performs a complete bet round
returns the player who last raised - if nobody raised, then the first one to check
"""
players_round = list(self._game_players.round(dealer_id))
if len(players_round) == 0:
raise GameError("No active players in this game")
# The dealer might be inactive. Moving to the first active player
dealer = players_round[0]
for k, player in enumerate(players_round):
if player.id not in bets:
bets[player.id] = 0
if bets[player.id] < 0 or (k > 0 and bets[player.id] < bets[players_round[k - 1].id]):
# Ensuring the bets dictionary makes sense
raise ValueError("Invalid bets dictionary")
best_player = None
while dealer is not None and dealer != best_player:
next_player = self._game_players.get_next(dealer.id)
max_bet = self._get_max_bet(dealer, bets)
min_bet = self._get_min_bet(dealer, bets)
if max_bet == 0.0:
# No bet required to this player (either he is all-in or all other players are all-in)
bet = 0.0
else:
# This player isn't all in, and there's at least one other player who is not all-in
bet = get_bet_function(player=dealer, min_bet=min_bet, max_bet=max_bet, bets=bets)
if bet is None:
self._game_players.remove(dealer.id)
elif bet == -1:
self._game_players.fold(dealer.id)
else:
if bet < min_bet or bet > max_bet:
raise ValueError("Invalid bet")
dealer.take_money(bet)
bets[dealer.id] += bet
if best_player is None or bet > min_bet:
best_player = dealer
if on_bet_function:
on_bet_function(dealer, bet, min_bet, max_bet, bets)
dealer = next_player
got_best.set()
return best_player
It looks like it works for a few calls of the ping, but then fails. I get the following in the logger:
2020-12-18T16:27:33.156525+00:00 app[web.1]: [2020-12-18 16:27:33,156] INFO in player_client: player 1853f194-bdf3-4abd-9743-47a1757a6bd8: connected to server 18bb7be6-e48e-4175-b221-b84b25bb6dee
2020-12-18T16:27:33.621393+00:00 heroku[router]: at=info method=GET path="/static/images/cards-medium.png" host=bourkesgarage.herokuapp.com request_id=10b78584-89f6-4a8f-8b49-7ace453ed43c fwd="81.170.56.125" dyno=web.1 connect=0ms service=92ms status=200 bytes=38539 protocol=http
2020-12-18T16:27:33.448527+00:00 heroku[router]: at=info method=GET path="/static/images/chips-small.png" host=bourkesgarage.herokuapp.com request_id=1264b65b-e84e-4f8b-91d4-ac77a893a8d6 fwd="81.170.56.125" dyno=web.1 connect=1ms service=11ms status=200 bytes=2368 protocol=http
2020-12-18T16:27:33.609812+00:00 heroku[router]: at=info method=GET path="/static/images/chips-50.png" host=bourkesgarage.herokuapp.com request_id=a112fed6-b7a1-4d9e-bf25-2ead196e0b03 fwd="81.170.56.125" dyno=web.1 connect=0ms service=76ms status=200 bytes=4834 protocol=http
2020-12-18T16:27:33.625784+00:00 heroku[router]: at=info method=GET path="/static/images/cards-small.png" host=bourkesgarage.herokuapp.com request_id=17cb122c-49c1-4035-a1f1-663a5d930cff fwd="81.170.56.125" dyno=web.1 connect=29ms service=137ms status=200 bytes=16891 protocol=http
2020-12-18T16:27:44.432880+00:00 app[worker.1]: Left the loop here
2020-12-18T16:27:50.951498+00:00 heroku[router]: at=info method=GET path="/static/images/cards-large.png" host=bourkesgarage.herokuapp.com request_id=2dc4482e-3b59-4c38-8048-831ca877b36e fwd="81.170.56.125" dyno=web.1 connect=14ms service=57ms status=200 bytes=77043 protocol=http
2020-12-18T16:28:31.735339+00:00 app[worker.1]: list of players [<poker.player_server.PlayerServer object at 0x7f6af3672820>, <poker.player_server.PlayerServer object at 0x7f6af368d610>, <poker.player_server.PlayerServer object at 0x7f6af3672370>] <class 'poker.player_server.PlayerServer'>
2020-12-18T16:28:31.735403+00:00 app[worker.1]: in bet_round - gonna ping player player 0e622560-cd2a-4b40-8847-ff4188c10893
2020-12-18T16:28:31.916022+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:28:31.916052+00:00 app[worker.1]: in bet_round - gonna ping player player 1853f194-bdf3-4abd-9743-47a1757a6bd8
2020-12-18T16:28:32.137961+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:28:32.137986+00:00 app[worker.1]: in bet_round - gonna ping player player c49273b8-1cc7-4794-8f8e-875aa6e8b01a
2020-12-18T16:28:32.258569+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:12.258811+00:00 app[worker.1]: list of players [<poker.player_server.PlayerServer object at 0x7f6af3672820>, <poker.player_server.PlayerServer object at 0x7f6af368d610>, <poker.player_server.PlayerServer object at 0x7f6af3672370>] <class 'poker.player_server.PlayerServer'>
2020-12-18T16:29:12.258845+00:00 app[worker.1]: in bet_round - gonna ping player player 0e622560-cd2a-4b40-8847-ff4188c10893
2020-12-18T16:29:12.470429+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:12.470437+00:00 app[worker.1]: in bet_round - gonna ping player player 1853f194-bdf3-4abd-9743-47a1757a6bd8
2020-12-18T16:29:12.576479+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:12.576555+00:00 app[worker.1]: in bet_round - gonna ping player player c49273b8-1cc7-4794-8f8e-875aa6e8b01a
2020-12-18T16:29:12.684640+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:52.684880+00:00 app[worker.1]: list of players [<poker.player_server.PlayerServer object at 0x7f6af3672820>, <poker.player_server.PlayerServer object at 0x7f6af368d610>, <poker.player_server.PlayerServer object at 0x7f6af3672370>] <class 'poker.player_server.PlayerServer'>
2020-12-18T16:29:52.684925+00:00 app[worker.1]: in bet_round - gonna ping player player 0e622560-cd2a-4b40-8847-ff4188c10893
2020-12-18T16:29:52.816735+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:52.816744+00:00 app[worker.1]: in bet_round - gonna ping player player 1853f194-bdf3-4abd-9743-47a1757a6bd8
2020-12-18T16:29:53.038439+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:29:53.038495+00:00 app[worker.1]: in bet_round - gonna ping player player c49273b8-1cc7-4794-8f8e-875aa6e8b01a
2020-12-18T16:29:53.226873+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:30:33.227221+00:00 app[worker.1]: list of players [<poker.player_server.PlayerServer object at 0x7f6af3672820>, <poker.player_server.PlayerServer object at 0x7f6af368d610>, <poker.player_server.PlayerServer object at 0x7f6af3672370>] <class 'poker.player_server.PlayerServer'>
2020-12-18T16:30:33.227384+00:00 app[worker.1]: in bet_round - gonna ping player player 0e622560-cd2a-4b40-8847-ff4188c10893
2020-12-18T16:30:33.366195+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:30:33.366305+00:00 app[worker.1]: in bet_round - gonna ping player player 1853f194-bdf3-4abd-9743-47a1757a6bd8
2020-12-18T16:30:35.374865+00:00 app[worker.1]: ERROR:root:Unable to ping player 1853f194-bdf3-4abd-9743-47a1757a6bd8: Timed out
2020-12-18T16:30:35.377289+00:00 app[worker.1]: Unable to ping, gonna remove player
2020-12-18T16:30:35.377432+00:00 app[worker.1]: in bet_round - gonna ping player player c49273b8-1cc7-4794-8f8e-875aa6e8b01a
2020-12-18T16:30:35.514655+00:00 app[worker.1]: successfully pinged player
2020-12-18T16:30:35.382919+00:00 app[web.1]: [2020-12-18 16:30:35,382] INFO in client_web: player 1853f194-bdf3-4abd-9743-47a1757a6bd8 connection closed
With this error towards the bottom.
2020-12-18T16:30:35.374865+00:00 app[worker.1]: ERROR:root:Unable to ping player 1853f194-bdf3-4abd-9743-47a1757a6bd8: Timed out
I don't suppose at a glance you can tell what I'm doing wrong, or if this is entirely the wrong way top go about it all together?
Thanks!
Ah - I have found that there is at least one reason why this approach won't work. The player can bet while the server is expecting a pong
message, and it'll return an error. If you spot any other issues feel free to point them out.
Hi @epifab - I have this sorted. I'll send a PR once I get a chance to play about with it a bit more & tidy it up.
hi there, sorry I couldn't help much here, but that sounds great. looking forward to it
hi @connorourke, I just realised that it might be a lot easier if a ping message was either sent by the JS client or the webapp directly, without having to hook this up to the main game logic
Hi @epifab - sorry, I'm back to work, so havent had time to look at this. The fix I tested myself unceremoniously fell over when I got other people together for a game. Not sure what the problem is.
Not having to hook this up to the main game logic certainly sounds simpler - I'll have a think about doing it that way when I get the time.