Skip to content

Remove Session.Touch() — TTL refresh belongs in the storage layer #4320

@jerm-dro

Description

@jerm-dro

Background

Session.Touch() updates an in-memory updated timestamp on every Manager.Get() call (manager.go:214). This timestamp is used by LocalStorage.DeleteExpired() to determine which sessions have gone stale.

For Redis-backed sessions, Touch() is dead weight — RedisStorage.Load already uses GETEX to atomically refresh the key TTL during the read. The in-memory timestamp update on the deserialized object is never persisted back to Redis, so it has no effect.

PR #4294 ("Drop RoutingStorage and Touch") removed the Storage-level Touch method for exactly this reason — it leaked a Redis-specific TTL concern onto a shared interface. The same reasoning applies to the Session-level Touch: TTL refresh is a storage concern, not a session-object concern.

What exists today

  • Session interface declares Touch() (manager.go)
  • ProxySession.Touch() updates s.updated = time.Now() under a mutex (proxy_session.go:81)
  • Manager.Get() calls sess.Touch() on every lookup (manager.go:214)
  • LocalStorage.DeleteExpired() checks session.UpdatedAt() against a cutoff time
  • All mock session setups in tests must include .EXPECT().Touch().AnyTimes()

What needs to happen

  1. Move the timestamp update into LocalStorage.Load — when LocalStorage returns a session, it should update the session's timestamp itself (or track last-access time internally), rather than relying on the caller to call Touch().
  2. Remove Touch() from the Session interface — this is a breaking interface change, so all implementations and mocks need updating.
  3. Remove the sess.Touch() call from Manager.Get() — each storage backend handles TTL/freshness internally (GETEX for Redis, timestamp update for local).
  4. Clean up mock expectations — remove all .EXPECT().Touch().AnyTimes() lines from test files.
  5. Verify LocalStorage.DeleteExpired still works — ensure the local backend's cleanup still correctly identifies stale sessions after the refactor.

Open questions

What happens to UpdatedAt? If Touch() is removed from the Session interface, nothing updates the updated field on the session object. UpdatedAt() is part of the Session interface and may have callers that expect it to reflect last-access time. Options:

  • Have LocalStorage.Load call Touch() internally (keeps the field accurate for local, still meaningless for Redis)
  • Remove UpdatedAt() from the Session interface entirely and let each storage backend track last-access time opaquely
  • Keep UpdatedAt but accept it only reflects creation/store time, not last access

Should CreatedAt/UpdatedAt live on the session object at all? These timestamps are lifecycle metadata used for expiry decisions — arguably a storage concern, not a property of the session's domain data. Redis tracks TTL natively and never consults these fields. LocalStorage could track last-access time in a wrapper struct (e.g., map[string]{ session Session; lastAccess time.Time }) instead of requiring every Session implementation to carry timestamp fields. This would shrink the Session interface and remove the need for Touch, CreatedAt, and UpdatedAt from session implementations and mocks.

Why this matters

Metadata

Metadata

Assignees

Labels

choregoPull requests that update go code

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions