Compare commits

...
Author SHA1 Message Date
james.maesandClaude Opus 5.5 d4d5029691 fix: build startup and diagnostics system sessions with the instance
primeConfig created QSystemUserSession() before QContext.init, so
on a thread without a context (startup on [main]) the session had no
instance and no all-access keys, and kof22 logged "QInstance was not
supplied" once per pod. DiagnosticEventRecorder did the same.
Both now pass the instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 01:24:24 -05:00
james.maes 8ef66f6efe Merge pull request #63 from QRun-IO/feature/ui-api-1.6
feat(ui-api): extension for QQQ UI API 1.6.0 (operationIds, BasicErrorResponseV1, QQQ 4.1.0-RC.15)
2026-10-15 16:13:09 -05:00
4 changed files with 31 additions and 2 deletions

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ All notable changes to this project are documented here. The format follows Keep
Requires QQQ 4.1.0-RC.15 or later (central health checks, help bundles, the spec-managed UI API extension contract, and `ClusterCaches` for multi-node deployments).
### Fixed
- Priming config records at startup, and recording diagnostic events, build their system session with the instance, so it has its all-access keys. Startup logged "QInstance was not supplied when creating a QSystemUserSession" once per node.
- Multi-node deployments: a Quick Search config change (an index row, its fields or the settings row, from the admin UI or a config process) now clears the cached config on every node, not only the node that made the change. The runtime's config cache (the config store snapshot and settings, and the per-table enabled flags) is registered with QQQ `ClusterCaches` as `qbit-quick-search.config`, and `QuickSearchRuntime.invalidateConfig()` publishes a cluster invalidation. The 30 s and 60 s TTLs remain as the safety net. `QuickSearchFieldRules`' warn-once table set is registered as node-local. `QuickSearchClusterCachesTest` audits the package for unregistered static caches and covers a remote invalidation.
- Indexing bookkeeping is no longer record-audited: the `quick_search_index` runtime fields written by basepull, full reindex and real-time indexing (status, last basepull time, document count, last run, real-time error count) and the settings row's priming outcome (`lastPrimedAt`, summary) are written with `omitDmlAudit`. On a production host these wrote about 470 "Record was Edited during process: Update search index" audit rows an hour. Admin edits to an index, its fields or the settings are still audited.
- Real-time indexing failures now increase the table's error count by the number of failed events. Successful basepulls and full reindexes reset the count. Counter update failures are logged without failing the source write.
@@ -758,7 +758,7 @@ public class QuickSearchRuntime
{
return;
}
QContext.init(qInstance, new QSystemUserSession());
QContext.init(qInstance, new QSystemUserSession(qInstance));
try
{
configStore.tryPrime();
@@ -124,7 +124,7 @@ public class DiagnosticEventRecorder implements AutoCloseable
*******************************************************************************/
private void insert(QInstance qInstance, String tableName, QuickSearchDiagnosticEvent event)
{
QSession session = new QSystemUserSession();
QSession session = new QSystemUserSession(qInstance);
try
{
QContext.init(qInstance, session);
@@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData;
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
import com.kingsrook.qbits.quicksearch.model.QuickSearchIndex;
import com.kingsrook.qbits.quicksearch.model.QuickSearchIndexField;
import com.kingsrook.qbits.quicksearch.publisher.IndexEventPublisher;
@@ -223,4 +224,31 @@ class QuickSearchRuntimeTest extends BaseQuickSearchTest
failFast.close();
}
/*******************************************************************************
** Priming at startup without a QContext builds the system session with the
** start instance (so it has the all-access keys), not from the empty context.
*******************************************************************************/
@Test
void primeConfigWithoutAContextGivesTheSystemSessionItsInstance()
{
QInstance qInstance = QContext.getQInstance();
QuickSearchRuntime runtime = new QuickSearchRuntime(new QuickSearchQBitConfig().withBackendName(TEST_BACKEND_NAME).withOpensearchHost("127.0.0.1").withOpensearchPort(1).withOpensearchIndexName("t"));
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(QSystemUserSession.class);
try
{
runtime.setStartInstance(qInstance);
QContext.clear();
runtime.primeConfig();
}
finally
{
QLogger.deactivateCollectingLoggerForClass(QSystemUserSession.class);
QContext.init(qInstance, new QSystemUserSession(qInstance));
runtime.close();
}
assertThat(collectingLogger.getCollectedMessages()).noneSatisfy(m -> assertThat(m.getMessage()).contains("QInstance was not supplied"));
}
}