Where a plugin implements both SSN_CLOSE_HOOK and TXN_CLOSE_HOOK, the SSN_CLOSE_HOOK is called first of the two. This messes up normal cleanups!
Register a SSN_START event globally
In the SSN START, add a TXN_START and a SSN_CLOSE
In the TXN START, add a TXN_CLOSE
Stepping through, I see the order of events actually called, for the simple case of a one-off HTTP request with no keepalive:
Whoops, SSN_END cleaned up the SSN context, leaving dangling pointers in the TXN!