SharedTempStore as session storage over DatabaseSessionStore
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3585861. --> Reported by: [mglaman](https://www.drupal.org/user/2416470) >>> <h3 id="summary-problem-motivation">Problem/Motivation</h3> <p><code>DatabaseSessionStore</code> is the only <code>SessionStoreInterface</code> implementation in <code>mcp_server</code>. It requires a custom <code>mcp_session_queue</code> database table, manages its own <code>expires_at</code> column, and relies on <code>DbSessionManager::gc()</code> being called externally to delete expired rows. Sites that already run Drupal's key-value expiry system (used by <code>SharedTempStore</code>) gain nothing from this parallel infrastructure they pay for an extra table and manual GC wiring that Drupal's tempstore subsystem already provides for free.</p> <p>This is what we implemented, offering as a suggestion.</p> <h3 id="summary-proposed-resolution">Proposed resolution</h3> <p>Add a <code>SharedTempStoreSessionStore</code> class that implements <code>SessionStoreInterface</code> using Drupal's <code>SharedTempStoreFactory</code>. Drupal's tempstore layer handles expiry automatically via its own GC, so <code>gc()</code> becomes a no-op. No custom schema, no cron hook.</p> <p><strong>PHP</strong></p> <p>Create <code>src/Session/SharedTempStoreSessionStore.php</code>:</p> <pre><pre><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br><br></span><span style="color: #007700">declare(</span><span style="color: #0000BB">strict_types</span><span style="color: #007700">=</span><span style="color: #0000BB">1</span><span style="color: #007700">);<br><br>namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">mcp_server</span><span style="color: #007700">\</span><span style="color: #0000BB">Session</span><span style="color: #007700">;<br><br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">TempStore</span><span style="color: #007700">\</span><span style="color: #0000BB">SharedTempStore</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">TempStore</span><span style="color: #007700">\</span><span style="color: #0000BB">SharedTempStoreFactory</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">TempStore</span><span style="color: #007700">\</span><span style="color: #0000BB">TempStoreException</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Mcp</span><span style="color: #007700">\</span><span style="color: #0000BB">Server</span><span style="color: #007700">\</span><span style="color: #0000BB">Session</span><span style="color: #007700">\</span><span style="color: #0000BB">SessionStoreInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Symfony</span><span style="color: #007700">\</span><span style="color: #0000BB">Component</span><span style="color: #007700">\</span><span style="color: #0000BB">Uid</span><span style="color: #007700">\</span><span style="color: #0000BB">Uuid</span><span style="color: #007700">;<br><br>final </span><span style="color: #0000BB">readonly </span><span style="color: #007700">class </span><span style="color: #0000BB">SharedTempStoreSessionStore </span><span style="color: #007700">implements </span><span style="color: #0000BB">SessionStoreInterface </span><span style="color: #007700">{<br><br>&nbsp; private const </span><span style="color: #0000BB">string COLLECTION </span><span style="color: #007700">= </span><span style="color: #DD0000">'mcp_sessions'</span><span style="color: #007700">;<br><br>&nbsp; public function </span><span style="color: #0000BB">__construct</span><span style="color: #007700">(<br>&nbsp;&nbsp;&nbsp; private </span><span style="color: #0000BB">SharedTempStoreFactory $tempStoreFactory</span><span style="color: #007700">,<br>&nbsp; ) {}<br><br>&nbsp; public function </span><span style="color: #0000BB">exists</span><span style="color: #007700">(</span><span style="color: #0000BB">Uuid $id</span><span style="color: #007700">): </span><span style="color: #0000BB">bool </span><span style="color: #007700">{<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">read</span><span style="color: #007700">(</span><span style="color: #0000BB">$id</span><span style="color: #007700">) !== </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br>&nbsp; }<br><br>&nbsp; public function </span><span style="color: #0000BB">read</span><span style="color: #007700">(</span><span style="color: #0000BB">Uuid $id</span><span style="color: #007700">): </span><span style="color: #0000BB">string</span><span style="color: #007700">|</span><span style="color: #0000BB">false </span><span style="color: #007700">{<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">store</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">get</span><span style="color: #007700">(</span><span style="color: #0000BB">$id</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">toRfc4122</span><span style="color: #007700">()) ?? </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br>&nbsp; }<br><br>&nbsp; public function </span><span style="color: #0000BB">write</span><span style="color: #007700">(</span><span style="color: #0000BB">Uuid $id</span><span style="color: #007700">, </span><span style="color: #0000BB">string $data</span><span style="color: #007700">): </span><span style="color: #0000BB">bool </span><span style="color: #007700">{<br>&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">store</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">set</span><span style="color: #007700">(</span><span style="color: #0000BB">$id</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">toRfc4122</span><span style="color: #007700">(), </span><span style="color: #0000BB">$data</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch (</span><span style="color: #0000BB">TempStoreException</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br><br>&nbsp; public function </span><span style="color: #0000BB">destroy</span><span style="color: #007700">(</span><span style="color: #0000BB">Uuid $id</span><span style="color: #007700">): </span><span style="color: #0000BB">bool </span><span style="color: #007700">{<br>&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">store</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">delete</span><span style="color: #007700">(</span><span style="color: #0000BB">$id</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">toRfc4122</span><span style="color: #007700">());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch (</span><span style="color: #0000BB">TempStoreException</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br><br>&nbsp; public function </span><span style="color: #0000BB">gc</span><span style="color: #007700">(): array {<br>&nbsp;&nbsp;&nbsp; return [];<br>&nbsp; }<br><br>&nbsp; private function </span><span style="color: #0000BB">store</span><span style="color: #007700">(): </span><span style="color: #0000BB">SharedTempStore </span><span style="color: #007700">{<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">tempStoreFactory</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">get</span><span style="color: #007700">(</span><span style="color: #0000BB">self</span><span style="color: #007700">::</span><span style="color: #0000BB">COLLECTION</span><span style="color: #007700">);<br>&nbsp; }<br><br>}<br></span><span style="color: #0000BB">?&gt;</span></span></pre></pre><h3 id="summary-api-changes">API changes</h3> <h3 id="summary-ai-usage">AI usage (if applicable)</h3> <p>[X AI Assisted Issue<br> This issue was generated with AI assistance, but was reviewed and refined by the creator.</p> <p>[ ] AI Assisted Code<br> This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.</p> <p>[ ] AI Generated Code<br> This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.</p> <p>[ ] Vibe Coded<br> This code was generated by an AI and has only been functionally tested.</p>
issue