Skip to content

session: a fresh, never-modified session is persisted on every cookieless request (unbounded store growth under anonymous load) #487

Description

@FumingPower3925

Summary

middleware/session persists a fresh, never-modified session on every request that arrives without a session cookie. The post-handler guard is:

} else if sess.destroyed {
    // clear cookie
} else if sess.modified || sess.fresh {   // <-- sess.fresh alone triggers a store write
    ...
    saveErr := kv.Set(reqCtx, sess.id, buf, expiry)
    ...
}

A cookieless request takes the !loaded branch, which sets sess.fresh = true and a new random id. If the handler never touches the session (sess.modified stays false), the middleware still writes a brand-new row to the store and sets a cookie.

Impact

Any endpoint fronted by the session middleware creates one store row per anonymous request. Under load from clients that don't carry the cookie (health checks, crawlers, API clients, a benchmark/validation walker) the session store grows without bound.

Observed in probatorium's driver_postgres validation refapp: a tier-1 walker (30 concurrent, cookieless, ~3,500 req/s) grew celeris_sessions by ~5 MB/s (≈one 1.5 KB row per request), reaching 1.1 GB in 200 s and filling the fixture's tmpfs — while the users table it was actually exercising stayed at 8 MB. The session store's 5-minute expiry cleanup does not keep up (rows are created far faster than they expire, and within a burst none have expired yet).

Expected

Persist a fresh session only if the handler actually wrote to it (sess.modified), i.e. lazy session creation — the common behaviour for session libraries. A fresh-but-empty session should not hit the store or set a cookie. If eager creation is ever desired it should be opt-in (e.g. a Config.SaveUnmodified / AlwaysCreate flag), not the default.

Repro

Install session.New(session.Config{Store: <any store>}) globally; send N requests with no session cookie to a handler that never calls c.Session(). The store ends up with N rows.

Workaround

Don't install the session middleware on routes that don't use it (the probatorium refapp was fixed this way).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions