Datasource session storage permanently stops updating once a duplicate row exists (unique index includes expires)

Hi!

Once cf_session_data contains two rows with the same cfid and name, that session is never written to the database again. Restarting Lucee doesn’t help, since the rows stay in the table.

When Lucee creates the storage table, it adds this index (Ansi92.java#L124 (Lucee/core/src/main/java/lucee/runtime/type/scope/storage/db/Ansi92.java at 7.1 · lucee/Lucee · GitHub)):

CREATE UNIQUE INDEX ix_cf_session_data ON cf_session_data (cfid, name, expires)

Because expires is a millisecond timestamp, this index doesn’t prevent two rows for the same cfid and name.

With two rows, every UPDATE … WHERE cfid=? AND name=? tries to set the same expires and data on both. They would become identical on (cfid, name, expires), so the unique index rejects the statement. The error only goes to scope.log, and the session stays stuck until the cleaner deletes the expired rows.

Ansi92.update() already expects duplicates: if (recordsAffected > 1) deletes them and re-inserts. But that branch is never reached, because the UPDATE fails first.

I created the duplicate manually while testing, so I haven’t seen it happen on its own. But update() does UPDATE, then INSERT if 0 rows, with no lock or transaction, so two concurrent requests on a session without a row could both insert.

Proposed fix: make the unique index (cfid, name) only.