Skip to content

Commit

Permalink
Clean empty, day old, lobbies on a 30min timer.
Browse files Browse the repository at this point in the history
(also snuck in the disabling of websocket compression, see coder/websocket#218 (comment))
  • Loading branch information
koenbollen committed Oct 4, 2023
1 parent acd7e42 commit f7c1175
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
32 changes: 22 additions & 10 deletions internal/signaling/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"net/http"
"strings"
"sync"
"time"

Expand All @@ -19,12 +18,32 @@ import (

const MaxConnectionTime = 1 * time.Hour

const LobbyCleanInterval = 30 * time.Minute
const LobbyCleanThreshold = 24 * time.Hour

func Handler(ctx context.Context, store stores.Store, cloudflare *cloudflare.CredentialsClient) (*sync.WaitGroup, http.HandlerFunc) {
manager := &TimeoutManager{
Store: store,
}
go manager.Run(ctx)

go func() {
logger := logging.GetLogger(ctx)
ticker := time.NewTicker(LobbyCleanInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
logger.Info("cleaning empty lobbies")
if err := store.CleanEmptyLobbies(ctx, time.Now().Add(-LobbyCleanThreshold)); err != nil {
logger.Error("failed to clean empty lobbies", zap.Error(err))
}
case <-ctx.Done():
return
}
}
}()

wg := &sync.WaitGroup{}
return wg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
Expand All @@ -34,17 +53,10 @@ func Handler(ctx context.Context, store stores.Store, cloudflare *cloudflare.Cre
ctx, cancel := context.WithTimeout(ctx, MaxConnectionTime)
defer cancel()

userAgentLower := strings.ToLower(r.Header.Get("User-Agent"))
isSafari := strings.Contains(userAgentLower, "safari") && !strings.Contains(userAgentLower, "chrome") && !strings.Contains(userAgentLower, "android")
acceptOptions := &websocket.AcceptOptions{
// Allow any origin/game to connect.
InsecureSkipVerify: true,
}

if isSafari {
acceptOptions.CompressionMode = websocket.CompressionDisabled
InsecureSkipVerify: true, // Allow any origin/game to connect.
CompressionMode: websocket.CompressionDisabled,
}

conn, err := websocket.Accept(w, r, acceptOptions)
if err != nil {
util.ErrorAndAbort(w, r, http.StatusBadRequest, "", err)
Expand Down
41 changes: 30 additions & 11 deletions internal/signaling/stores/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,12 @@ func (s *PostgresStore) CreateLobby(ctx context.Context, game, lobbyCode, peerID
logger.Warn("peer id too long", zap.String("peerID", peerID))
return ErrInvalidPeerID
}
now := util.Now(ctx)
res, err := s.DB.Exec(ctx, `
INSERT INTO lobbies (code, game, public)
VALUES ($1, $2, true)
INSERT INTO lobbies (code, game, public, created_at, updated_at)
VALUES ($1, $2, true, $3, $3)
ON CONFLICT DO NOTHING
`, lobbyCode, game)
`, lobbyCode, game, now)
if err != nil {
return err
}
Expand All @@ -171,6 +172,9 @@ func (s *PostgresStore) JoinLobby(ctx context.Context, game, lobbyCode, peerID s
logger.Warn("peer id too long", zap.String("peerID", peerID))
return nil, ErrInvalidPeerID
}

now := util.Now(ctx)

tx, err := s.DB.Begin(ctx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -200,10 +204,12 @@ func (s *PostgresStore) JoinLobby(ctx context.Context, game, lobbyCode, peerID s

_, err = tx.Exec(ctx, `
UPDATE lobbies
SET peers = array_append(peers, $1)
WHERE code = $2
AND game = $3
`, peerID, lobbyCode, game)
SET
peers = array_append(peers, $1),
updated_at = $2
WHERE code = $3
AND game = $4
`, peerID, now, lobbyCode, game)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -232,14 +238,18 @@ func (s *PostgresStore) IsPeerInLobby(ctx context.Context, game, lobbyCode, peer
}

func (s *PostgresStore) LeaveLobby(ctx context.Context, game, lobbyCode, peerID string) ([]string, error) {
now := util.Now(ctx)

var peerlist []string
err := s.DB.QueryRow(ctx, `
UPDATE lobbies
SET peers = array_remove(peers, $1)
WHERE code = $2
AND game = $3
SET
peers = array_remove(peers, $1),
updated_at = $2
WHERE code = $3
AND game = $4
RETURNING peers
`, peerID, lobbyCode, game).Scan(&peerlist)
`, peerID, now, lobbyCode, game).Scan(&peerlist)
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return nil, err
}
Expand Down Expand Up @@ -387,3 +397,12 @@ func (s *PostgresStore) ClaimNextTimedOutPeer(ctx context.Context, threshold tim

return true, tx.Commit(ctx)
}

func (s *PostgresStore) CleanEmptyLobbies(ctx context.Context, olderThan time.Time) error {
_, err := s.DB.Exec(ctx, `
DELETE FROM lobbies
WHERE updated_at < $1
AND peers = '{}'
`, olderThan)
return err
}
2 changes: 2 additions & 0 deletions internal/signaling/stores/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type Store interface {
TimeoutPeer(ctx context.Context, peerID, secret, gameID string, lobbies []string) error
ReconnectPeer(ctx context.Context, peerID, secret, gameID string) (bool, error)
ClaimNextTimedOutPeer(ctx context.Context, threshold time.Duration, callback func(peerID, gameID string, lobbies []string) error) (bool, error)

CleanEmptyLobbies(ctx context.Context, olderThan time.Time) error
}

type Lobby struct {
Expand Down

0 comments on commit f7c1175

Please sign in to comment.