Skip to content

Commit aac100f

Browse files
authored
Clean empty, day old, lobbies on a 30min timer. (#63)
Currently all lobbies stay in the database even if they are empty. This change introduces a simple timer that deletes day old empty lobbies every 30 minutes. This is needed for better lobby listing (filters, private etc) in the future. Also snuck in the disabling of websocket compression, see coder/websocket#218 (comment)
1 parent 01d2791 commit aac100f

File tree

3 files changed

+54
-21
lines changed

3 files changed

+54
-21
lines changed

Diff for: internal/signaling/handler.go

+22-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"net/http"
7-
"strings"
87
"sync"
98
"time"
109

@@ -19,12 +18,32 @@ import (
1918

2019
const MaxConnectionTime = 1 * time.Hour
2120

21+
const LobbyCleanInterval = 30 * time.Minute
22+
const LobbyCleanThreshold = 24 * time.Hour
23+
2224
func Handler(ctx context.Context, store stores.Store, cloudflare *cloudflare.CredentialsClient) (*sync.WaitGroup, http.HandlerFunc) {
2325
manager := &TimeoutManager{
2426
Store: store,
2527
}
2628
go manager.Run(ctx)
2729

30+
go func() {
31+
logger := logging.GetLogger(ctx)
32+
ticker := time.NewTicker(LobbyCleanInterval)
33+
defer ticker.Stop()
34+
for {
35+
select {
36+
case <-ticker.C:
37+
logger.Info("cleaning empty lobbies")
38+
if err := store.CleanEmptyLobbies(ctx, time.Now().Add(-LobbyCleanThreshold)); err != nil {
39+
logger.Error("failed to clean empty lobbies", zap.Error(err))
40+
}
41+
case <-ctx.Done():
42+
return
43+
}
44+
}
45+
}()
46+
2847
wg := &sync.WaitGroup{}
2948
return wg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3049
ctx := r.Context()
@@ -34,17 +53,10 @@ func Handler(ctx context.Context, store stores.Store, cloudflare *cloudflare.Cre
3453
ctx, cancel := context.WithTimeout(ctx, MaxConnectionTime)
3554
defer cancel()
3655

37-
userAgentLower := strings.ToLower(r.Header.Get("User-Agent"))
38-
isSafari := strings.Contains(userAgentLower, "safari") && !strings.Contains(userAgentLower, "chrome") && !strings.Contains(userAgentLower, "android")
3956
acceptOptions := &websocket.AcceptOptions{
40-
// Allow any origin/game to connect.
41-
InsecureSkipVerify: true,
42-
}
43-
44-
if isSafari {
45-
acceptOptions.CompressionMode = websocket.CompressionDisabled
57+
InsecureSkipVerify: true, // Allow any origin/game to connect.
58+
CompressionMode: websocket.CompressionDisabled,
4659
}
47-
4860
conn, err := websocket.Accept(w, r, acceptOptions)
4961
if err != nil {
5062
util.ErrorAndAbort(w, r, http.StatusBadRequest, "", err)

Diff for: internal/signaling/stores/postgres.go

+30-11
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,12 @@ func (s *PostgresStore) CreateLobby(ctx context.Context, game, lobbyCode, peerID
151151
logger.Warn("peer id too long", zap.String("peerID", peerID))
152152
return ErrInvalidPeerID
153153
}
154+
now := util.Now(ctx)
154155
res, err := s.DB.Exec(ctx, `
155-
INSERT INTO lobbies (code, game, public)
156-
VALUES ($1, $2, true)
156+
INSERT INTO lobbies (code, game, public, created_at, updated_at)
157+
VALUES ($1, $2, true, $3, $3)
157158
ON CONFLICT DO NOTHING
158-
`, lobbyCode, game)
159+
`, lobbyCode, game, now)
159160
if err != nil {
160161
return err
161162
}
@@ -171,6 +172,9 @@ func (s *PostgresStore) JoinLobby(ctx context.Context, game, lobbyCode, peerID s
171172
logger.Warn("peer id too long", zap.String("peerID", peerID))
172173
return nil, ErrInvalidPeerID
173174
}
175+
176+
now := util.Now(ctx)
177+
174178
tx, err := s.DB.Begin(ctx)
175179
if err != nil {
176180
return nil, err
@@ -200,10 +204,12 @@ func (s *PostgresStore) JoinLobby(ctx context.Context, game, lobbyCode, peerID s
200204

201205
_, err = tx.Exec(ctx, `
202206
UPDATE lobbies
203-
SET peers = array_append(peers, $1)
204-
WHERE code = $2
205-
AND game = $3
206-
`, peerID, lobbyCode, game)
207+
SET
208+
peers = array_append(peers, $1),
209+
updated_at = $2
210+
WHERE code = $3
211+
AND game = $4
212+
`, peerID, now, lobbyCode, game)
207213
if err != nil {
208214
return nil, err
209215
}
@@ -232,14 +238,18 @@ func (s *PostgresStore) IsPeerInLobby(ctx context.Context, game, lobbyCode, peer
232238
}
233239

234240
func (s *PostgresStore) LeaveLobby(ctx context.Context, game, lobbyCode, peerID string) ([]string, error) {
241+
now := util.Now(ctx)
242+
235243
var peerlist []string
236244
err := s.DB.QueryRow(ctx, `
237245
UPDATE lobbies
238-
SET peers = array_remove(peers, $1)
239-
WHERE code = $2
240-
AND game = $3
246+
SET
247+
peers = array_remove(peers, $1),
248+
updated_at = $2
249+
WHERE code = $3
250+
AND game = $4
241251
RETURNING peers
242-
`, peerID, lobbyCode, game).Scan(&peerlist)
252+
`, peerID, now, lobbyCode, game).Scan(&peerlist)
243253
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
244254
return nil, err
245255
}
@@ -392,3 +402,12 @@ func (s *PostgresStore) ClaimNextTimedOutPeer(ctx context.Context, threshold tim
392402

393403
return true, tx.Commit(ctx)
394404
}
405+
406+
func (s *PostgresStore) CleanEmptyLobbies(ctx context.Context, olderThan time.Time) error {
407+
_, err := s.DB.Exec(ctx, `
408+
DELETE FROM lobbies
409+
WHERE updated_at < $1
410+
AND peers = '{}'
411+
`, olderThan)
412+
return err
413+
}

Diff for: internal/signaling/stores/shared.go

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type Store interface {
2929
TimeoutPeer(ctx context.Context, peerID, secret, gameID string, lobbies []string) error
3030
ReconnectPeer(ctx context.Context, peerID, secret, gameID string) (bool, []string, error)
3131
ClaimNextTimedOutPeer(ctx context.Context, threshold time.Duration, callback func(peerID, gameID string, lobbies []string) error) (bool, error)
32+
33+
CleanEmptyLobbies(ctx context.Context, olderThan time.Time) error
3234
}
3335

3436
type Lobby struct {

0 commit comments

Comments
 (0)