perf(managed-agent): avoid redundant session root materialization (#14557)

Keep the exclusive root UPSERT as the single initialization path before creating the Session event stream.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
易良 2026-10-18 19:01:55 +08:00 • committed by GitHub
parent e584d4d197
commit 2f2e7256fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 8 additions and 42 deletions

View file

@ -2843,42 +2843,12 @@ public class ManagedSessionStore {
return sha256(writerToken.getBytes(StandardCharsets.UTF_8));
}
private boolean insertSessionRoot(String tenantId, String sessionId) {
long now = epochMillis(databaseNow());
String tenantKey = com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(tenantId);
String sessionKey = com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(sessionId);
int inserted = jdbc.update(
"INSERT IGNORE INTO ma_session (tenant_id, session_id, session_kind, tenant_key,"
+ " session_key, session_scope_key, gmt_create, gmt_modified) VALUES (?, ?,"
+ " 'CONTROL', ?, ?, ?, ?, ?)",
tenantId,
sessionId,
tenantKey,
sessionKey,
sessionScopeKey(tenantId, sessionId),
now,
now);
return inserted == 1;
}
private void ensureSessionRoot(String tenantId, String sessionId) {
// Take the root row exclusively first: ordinary writers lock it
// without a tenant guard (#13800), and the shared lock INSERT IGNORE
// takes on an existing row would otherwise upgrade behind one of
// them and deadlock; lockSessionRoot materializes the row with its
// keys, so the INSERT IGNORE below never inserts.
// without a tenant guard (#13800); keep that order before
// initializing the session event stream.
ToolPublicationRetentionStore.lockSessionRoot(jdbc, tenantId, sessionId);
insertSessionRoot(tenantId, sessionId);
materializeSessionRoot(tenantId, sessionId);
}
private void materializeSessionRoot(String tenantId, String sessionId) {
long now = epochMillis(databaseNow());
String tenantKey = JdbcRuntimeBindingRepository.harnessDrainKey(tenantId);
String sessionKey = JdbcRuntimeBindingRepository.harnessDrainKey(sessionId);
jdbc.update("UPDATE ma_session SET tenant_key = ?, session_key = ?, session_scope_key = ?"
+ " WHERE tenant_id = ? AND session_id = ? AND session_scope_key IS NULL",
tenantKey, sessionKey, sessionScopeKey(tenantId, sessionId), tenantId, sessionId);
jdbc.update(
"INSERT IGNORE INTO ma_event_stream (tenant_id, session_id, stream_kind, stream_id,"
+ " gmt_modified) VALUES (?, ?, 'SESSION', X'', ?)",

View file

@ -544,19 +544,15 @@ class ManagedSessionGuardConvoyIT {
}
/**
* Writer acquisition must hold the Session root exclusively before any
* shared lock on it. Since #13800 ordinary writers lock that root
* without a tenant guard, and on MariaDB a shared root lock upgraded
* behind one of their queued exclusive requests is an InnoDB 1213
* deadlock. The acquisition pauses right after ensureSessionRoot's
* INSERT IGNORE while an ordinary blockRecovery of the same Session
* queues on the root; both must then complete. (MySQL 8.4 lets the
* shared holder upgrade ahead of the queue, so only MariaDB fails
* without the exclusive-first order.)
* Writer acquisition must hold the Session root exclusively before an
* ordinary writer can queue on it. The acquisition pauses after
* the acquisition's first root UPSERT, then an ordinary blockRecovery of the
* same Session queues on the root; releasing the pause must let both
* complete without an InnoDB deadlock.
*/
@Test
void writerAcquisitionHoldsTheSessionRootExclusivelyAgainstOrdinaryWriters() throws Exception {
assertNoDeadlockAgainstOrdinaryWriter("INSERT IGNORE INTO ma_session (",
assertNoDeadlockAgainstOrdinaryWriter("INSERT INTO ma_session (",
(store, grant) -> store.acquireWriter(TENANT, "victim", TOKEN,
new AcquireWriterRequest(WORKSPACE, "writer-victim", 60_000L)));
}