Keep active IRC listener users from expiring (closes #121) #126
@@ -2243,7 +2243,7 @@ The database schema is managed via embedded SQL migration files in
|
||||
| `signing_key` | TEXT | Public signing key (empty string if unset) |
|
||||
| `away_message` | TEXT | Away message (empty string if not away) |
|
||||
| `created_at` | DATETIME | Session creation time |
|
||||
| `last_seen` | DATETIME | Last API request time |
|
||||
| `last_seen` | DATETIME | Last API request or IRC listener command time |
|
||||
|
||||
Index on `(uuid)`.
|
||||
|
||||
@@ -2258,7 +2258,7 @@ Index on `(uuid)`.
|
||||
| `ip` | TEXT | Real IP address of this client connection |
|
||||
| `hostname` | TEXT | Reverse DNS hostname of this client connection |
|
||||
| `created_at` | DATETIME | Client creation time |
|
||||
| `last_seen` | DATETIME | Last API request time |
|
||||
| `last_seen` | DATETIME | Last API request or IRC listener command time |
|
||||
|
||||
Indexes on `(token)` and `(session_id)`.
|
||||
|
||||
|
||||
+24
-3
@@ -204,21 +204,42 @@ func (database *Database) GetSessionByToken(
|
||||
)
|
||||
}
|
||||
|
||||
_ = database.UpdateLastSeen(ctx, sessionID, clientID)
|
||||
|
||||
return sessionID, clientID, nick, nil
|
||||
}
|
||||
|
||||
// UpdateLastSeen sets last_seen to now on a session and on
|
||||
// one of its clients, so that the idle cleanup keeps them.
|
||||
func (database *Database) UpdateLastSeen(
|
||||
ctx context.Context,
|
||||
sessionID, clientID int64,
|
||||
) error {
|
||||
now := time.Now()
|
||||
|
||||
_, _ = database.conn.ExecContext(
|
||||
_, err := database.conn.ExecContext(
|
||||
ctx,
|
||||
"UPDATE sessions SET last_seen = ? WHERE id = ?",
|
||||
now, sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"update session last_seen: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
_, _ = database.conn.ExecContext(
|
||||
_, err = database.conn.ExecContext(
|
||||
ctx,
|
||||
"UPDATE clients SET last_seen = ? WHERE id = ?",
|
||||
now, clientID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"update client last_seen: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return sessionID, clientID, nick, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSessionByNick returns session id for a given nick.
|
||||
|
||||
@@ -368,6 +368,17 @@ func (c *Conn) handleMessage(
|
||||
return
|
||||
}
|
||||
|
||||
// Every command is activity, so the idle cleanup must not
|
||||
// remove this user.
|
||||
err := c.database.UpdateLastSeen(
|
||||
ctx, c.sessionID, c.clientID,
|
||||
)
|
||||
if err != nil {
|
||||
c.log.Error(
|
||||
"failed to update last_seen", "error", err,
|
||||
)
|
||||
}
|
||||
|
||||
handler, ok := c.commands[msg.Command]
|
||||
if !ok {
|
||||
c.sendNumeric(
|
||||
|
||||
@@ -369,6 +369,39 @@ func TestPingPong(t *testing.T) {
|
||||
assertContains(t, lines, "PONG", "PONG response")
|
||||
}
|
||||
|
||||
// TestIdleCleanupKeepsActiveUser checks that a user who
|
||||
// sends commands is kept by the idle cleanup, and a user who
|
||||
// sends nothing is not.
|
||||
func TestIdleCleanupKeepsActiveUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const idleTimeout = 500 * time.Millisecond
|
||||
|
||||
env := newTestEnv(t)
|
||||
|
||||
active := env.dial(t)
|
||||
active.register("active")
|
||||
|
||||
idle := env.dial(t)
|
||||
idle.register("idle")
|
||||
|
||||
time.Sleep(idleTimeout)
|
||||
active.sendAndExpect("PING :still here", "PONG")
|
||||
|
||||
// The idle cleanup in internal/handlers removes exactly
|
||||
// the users this returns.
|
||||
stale, err := env.database.GetStaleOrphanSessions(
|
||||
t.Context(), time.Now().Add(-idleTimeout),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("get stale sessions: %v", err)
|
||||
}
|
||||
|
||||
if len(stale) != 1 || stale[0].Nick != "idle" {
|
||||
t.Errorf("cleanup removes %v, want only idle", stale)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinChannel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user