conexus 6.3.1__tar.gz → 6.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {conexus-6.3.1 → conexus-6.3.2}/PKG-INFO +1 -1
- {conexus-6.3.1 → conexus-6.3.2}/docs/rdr/README.md +2 -0
- {conexus-6.3.1 → conexus-6.3.2}/mcpb/pyproject.toml +2 -2
- {conexus-6.3.1 → conexus-6.3.2}/pyproject.toml +1 -1
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/CatalogRepository.java +222 -9
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/ChashRepository.java +22 -2
- conexus-6.3.2/service/src/main/java/dev/nexus/service/db/DeadlockRetry.java +107 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TaxonomyRepository.java +156 -67
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/CatalogHandler.java +70 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/TaxonomyHandler.java +44 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/VectorHandler.java +32 -4
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/PgVectorRepository.java +516 -47
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogRepositoryTest.java +224 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ChashRepositoryTest.java +20 -0
- conexus-6.3.2/service/src/test/java/dev/nexus/service/PgVectorEmbedSkipGcRaceTest.java +344 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgVectorRepositoryContractTest.java +20 -0
- conexus-6.3.2/service/src/test/java/dev/nexus/service/PgVectorUpsertDeadlockTest.java +202 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomyRepositoryTest.java +64 -1
- conexus-6.3.2/service/src/test/java/dev/nexus/service/db/DeadlockRetryTest.java +83 -0
- conexus-6.3.2/service/src/test/java/dev/nexus/service/vectors/PgVectorEmbedSkipIntegrationTest.java +673 -0
- conexus-6.3.2/service/src/test/java/dev/nexus/service/vectors/PgVectorEmbedSkipTest.java +131 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog.py +16 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_protocol.py +3 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/http_catalog_client.py +120 -12
- conexus-6.3.2/src/nexus/chunk_batcher.py +364 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/code_indexer.py +25 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/index.py +108 -13
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/service_cmd.py +92 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/binary_install.py +1 -1
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/catalog_write_shim.py +12 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/t2_client.py +2 -2
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/http_vector_client.py +179 -45
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_chash_index.py +9 -1
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_taxonomy_store.py +22 -3
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t3.py +16 -1
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc_indexer.py +28 -3
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/hook_registry.py +13 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/index_context.py +9 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/indexer.py +427 -63
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/indexer_utils.py +45 -5
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/core.py +30 -6
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp_infra.py +99 -12
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/prose_indexer.py +23 -0
- {conexus-6.3.1 → conexus-6.3.2}/.beads/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/.gitignore +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/LICENSE +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/agents/_shared/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/com.nexus.service.plist +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/com.nexus.t2.plist +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/com.nexus.t3.plist +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/nexus-service.service +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/nexus-t2.service +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/daemon/nexus-t3.service +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/hooks/scripts/routing/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/abstract-themes.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/analyze-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/citation-traversal.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/debug-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/document-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/find-by-author.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/hybrid-factual-lookup.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/plan-author-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/plan-inspect-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/plan-inspect-dimensions.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/plan-promote-propose.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/research-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/review-default.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/traverse-then-generate.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/builtin/type-scoped-search.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/dimensions.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/conexus/plans/purposes.yml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/data/calibration/rdr-109/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/docs/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/docs/migration/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/dt/scripts/Index Current Group in nx.applescript +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/dt/scripts/Index Selection in nx (Knowledge).applescript +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/dt/scripts/Index Selection in nx.applescript +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/mcpb/src/server.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/cron/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/launchd/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/rdr152-sandbox/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/spikes/s04-build/src/main/java/dev/nexus/spike/s04/Main.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/spikes/s04-build/src/main/resources/META-INF/native-image/reachability-metadata.json +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/spikes/s04-build/src/main/resources/db/changelog/db.changelog-master.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/scripts/spikes/s04-build/src/main/resources/logback.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/Main.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/NexusService.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/AspectRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/CollectionRegistry.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/MemoryRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/MigrationJobRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/PgSession.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/PlanRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/PoolerModeCheck.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/SchemaMigrator.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/ScratchRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/SessionPrincipal.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TelemetryRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TenantConstants.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TenantScope.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TokenCache.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TokenHashing.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/TokenStore.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/AspectHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/AuthFilter.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/ChashHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/HealthHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/HttpUtil.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/MemoryHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/MigrationHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/PlanHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/RequestContext.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/ScratchHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/SessionTokenHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/TelemetryHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/TokenAdminHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/VersionHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/http/WhoamiHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/jooq/binding/Vector.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/jooq/binding/VectorBinding.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/nativeimage/JooqRecordReflectionFeature.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/ChromaSchemeHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/ChunkTextFetcher.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/HttpsSchemeHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/ResolveResult.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/UnknownSchemeException.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/UriSchemeHandler.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/resolver/UriSchemeResolverRegistry.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/Bge768Embedder.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/CceEmbedder.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/ChromaQuotaValidator.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/ChromaRestClient.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/DimTables.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/EgressProxy.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/EmbedResult.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/Embedder.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/EmbedderRouter.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/EmbeddingModelUnavailableException.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/LocalChromaServer.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/OnnxEmbedder.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/TaxonomyCentroidRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/VectorRepository.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/vectors/VoyageEmbedder.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/META-INF/native-image/native-image.properties +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/META-INF/native-image/reflect-config.json +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/META-INF/native-image/resource-config.json +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/META-INF/native-image/traced/reachability-metadata.json +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/META-INF/nexus/release.properties +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/aspects-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/aspects-002-next-retry-at.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-002-hygiene.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-003-soft-delete.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-004-manifest-functions.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-005-collection-vector-stats.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-006-combined-query-functions.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-007-graph-hop-functions.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-008-combined-query-extensions.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-009-read-shape-views.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/catalog-011-collection-health-stale-age.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/chash-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/db.changelog-master.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/fk-001-catalog-cross-store.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/fk-002-collection-registry.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/fk-002-validate.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/fk-003-collection-registry-extra.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/fk-003-validate.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/grants-nexus-svc.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/memory-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/migration-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/perf-001-rename-collection-indexes.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/plans-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/role-001-nexus-svc.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/service-tokens-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/service-tokens-002-single-root-index.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/t1-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/taxonomy-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/taxonomy-002-centroids.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/taxonomy-003-doc-count-trigger.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/taxonomy-004-dedup-root-topics.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/telemetry-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/updated-at-001-triggers.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/vectors-001-baseline.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/db/changelog/vectors-002-trgm-index.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/main/resources/logback.xml +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/AspectOperatorQueryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/AspectRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/AuthFilterTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/Bge768ParityTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/Bge768ServiceEmbedIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/BridgeAddressFieldsTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogDeleteCollectionCascadeTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogDocumentCascadeTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogHandlerDeleteTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogHandlerRenameTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogRenameCollectionTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CatalogSchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ChashVectorConcurrencyTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ChunksRlsBehavioralTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CollectionRegistryFkExtraTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CollectionRegistryFkTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CollectionVectorStatsTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CombinedQueryParityIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/CombinedQueryParityTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/DualRunHarnessIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/EmbedParityTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/EmbeddingModeFailLoudTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ForeignKeyConstraintTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/GraphHopParityIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/GraphHopParityTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/HybridParityIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/HybridSelectiveGateTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ManifestFunctionsTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/MemoryHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/MemoryRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/MemorySchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgBouncerTenantIsolationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgContainerHelper.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgVectorCombinedQueryContractTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgVectorHybridSearchContractTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PgVectorServingContractTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PlanHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PlanRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/PlansSchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ReadShapeViewsTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ReferenceOnlyChunkUpsertTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/SchemaMigratorIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ScratchHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ScratchRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ScratchSchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ServiceBindHostTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ServiceIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/ServiceTokenSchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/SessionTokenHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/SoftDeleteTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomyCentroidHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomyCentroidRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomyCentroidSchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomyPersistHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TaxonomySchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TelemetryRepositoryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TelemetrySchemaLiquibaseTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TenantPoolingIsolationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TokenAdminHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/TokenBoundaryAdversarialTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/UpdatedAtTriggerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/VectorHandlerEmbeddingModeTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/VectorHandlerTokenUsageTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/VectorHybridHttpTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/VectorIntegrationTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/CatalogTsOrNullTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/CollectionRegistryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/PoolerModeCheckTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/RawSqlGateTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/TenantScopeAdmissionTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/db/TenantScopeGucAllowlistTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/AspectHandlerEnqueueErrorTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/CatalogHandlerImportEmptyBodyTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/CatalogHandlerManifestFkTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/MigrationHandlerIngestCloudTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/TaxonomyHandlerAssignFkTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/http/VersionHandlerReleaseVersionTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/jooq/binding/VectorRoundTripTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/nativeimage/JooqRecordReflectionFeatureTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/resolver/ChromaSchemeHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/resolver/HttpsSchemeHandlerTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/resolver/UriSchemeResolverRegistryTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/Bge768EmbedderMathTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/EgressProxyTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/EmbedderRouterBge768Test.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/EmbedderRouterVoyageOnlyTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/PgVectorRepositoryWherePredicateTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/java/dev/nexus/service/vectors/ReferenceOnlySqlShapeTest.java +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/service/src/test/resources/bge768/parity_reference.json +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/sn/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/sn/hooks/scripts/routing/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/_git_hooks_meta.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/_locking.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/_mineru_pid.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/_session_end_launcher.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/aspect_extractor.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/aspect_promotion.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/aspect_readers.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/aspect_worker.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/bib_enricher.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/bib_enricher_openalex.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/bib_extractor.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/AGENTS.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/auto_linker.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_backup.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_db.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_docs.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_git.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_links.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_owners.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_spans.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_sync.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/catalog_writes.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/collection_name.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/collections_owner_backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/consolidation.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/dedupe.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/dt_link_generator.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/event_log.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/events.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/factory.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/link_generator.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/manifest_backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/orphan_backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/projector.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/store_hook.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/synthesizer.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/tumbler.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/types.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/catalog/write_priority.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/checkpoint.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/chunk_identity.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/chunker.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/classifier.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/cli.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/collection_audit.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/collection_health.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/collection_rename.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/_helpers.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/_migration_prompt.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/_provision.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/aspects.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/backups.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/collections.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/doctor.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/integrity.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/links.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/maintenance.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/migration.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/orphan_backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/owners.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/remediation.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/catalog_cmds/report.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/collection.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/command_context.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/config_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/console.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/context_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/daemon.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/doc.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/doctor.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/dt.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/enrich.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/guided_upgrade_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/hook.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/hooks.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/init.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/memory.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/migrate_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/migration_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/mineru.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/plan.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/rdr.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/scratch.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/search_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/storage_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/t3.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/taxonomy_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/tenant_cmd.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/tier_status.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/uninstall.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/commands/upgrade.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/config.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/app.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/config.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/routes/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/routes/activity.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/routes/campaigns.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/routes/health.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/routes/partials.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/static/alpine.min.js +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/static/console.css +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/static/htmx.min.js +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/static/pico.min.css +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/activity/_detail.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/activity/_stream.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/activity/index.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/base.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/campaigns/detail.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/campaigns/index.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/health/_cards.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/templates/health/index.html +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/console/watchers.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/context.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/corpus.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/cross_encoder.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/AGENTS.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/T2_DAEMON_WIP.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/aspect_worker_daemon.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/binary_lifecycle.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/discovery.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/installer.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/service_registry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/spin_guard.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/storage_service_daemon.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/t1_lease.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/t2_daemon.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/daemon/t3_daemon.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/AGENTS.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/chroma_quotas.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/collection_purge.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/embed_migrate.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/http_scratch_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/local_ef.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/managed_endpoint.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/migrations.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/pg_bundle.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/pg_provision.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/service_bge_model.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/service_endpoint.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/storage_mode.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t1.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/_raw_handle_guard.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/_tuning.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/aspect_extraction_queue.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/aspects_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/catalog.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/catalog_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/catalog_taxonomy.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/chash_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/chash_index.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/document_aspects.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/document_highlights.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_aspect_queue.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_centroid_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_document_aspects_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_document_highlights_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_memory_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_plan_library.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_telemetry_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/http_token_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/memory_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/memory_store.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/plan_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/plan_library.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/taxonomy_compute.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/taxonomy_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/telemetry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t2/telemetry_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/db/t3_reidentify.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/devonthink.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/_common.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/citations.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/ref_scanner.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/render.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/resolvers.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/resolvers_corpus.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doc/tokens.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/doctor_search.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/dropped_writes.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/dt_writeback.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/errors.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/exporter.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/filters.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/formatters.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/frecency.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/glossary.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/health.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/hooks.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/languages.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/logging_setup.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/AGENTS.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/_first_run.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/_t1_state.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/catalog.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/devonthink.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp/plan_cache_registry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp_client/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp_client/core.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp_client/devonthink.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/mcp_server.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/md_chunker.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/merge_candidates.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/metadata_schema.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/banner.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/chroma_read.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/detection.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/driver.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/etl_registry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/guided_upgrade.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/manifest_check.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/migration_report.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/orchestrator.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/pregate.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/quiesce.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/route_coverage.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/sequencer.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/state.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/validation.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/vector_etl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/migration/verify_fill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/name_canaries.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/operators/aspect_sql.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/operators/dispatch.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/pdeathsig.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/pdf_chunker.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/pdf_extractor.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/phase_review_sentinel.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/pipeline_buffer.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/pipeline_stages.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/bundle.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/loader.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/match.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/matcher.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/promote.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/purposes.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/repair.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/runner.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/schema.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/scope.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/seed_loader.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/plans/session_cache.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/registry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/repo_identity.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/repos.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/retry.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/ripgrep_cache.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/routing_stats.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/salience.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/scoring.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/search_clusterer.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/search_engine.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/session.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/stage_timers.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/storage_boundary_lint.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/taxonomy.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/taxonomy_backfill.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/ttl.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/types.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/util/__init__.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/src/nexus/util/process_group.py +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/tests/cc-validation/README.md +0 -0
- {conexus-6.3.1 → conexus-6.3.2}/tests/e2e/t2-migration-sqlite/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conexus
|
|
3
|
-
Version: 6.3.
|
|
3
|
+
Version: 6.3.2
|
|
4
4
|
Summary: Self-hosted semantic search and knowledge management for LLM-driven development
|
|
5
5
|
Project-URL: Homepage, https://github.com/Hellblazer/nexus
|
|
6
6
|
Project-URL: Repository, https://github.com/Hellblazer/nexus
|
|
@@ -194,6 +194,8 @@ An RDR (Research-Design-Review) is a short document that records a technical dec
|
|
|
194
194
|
| [RDR-176](rdr-176-survivable-managed-migration-readiness.md) | Survivable Managed-Migration Readiness: Batched ETL, First-Class Downgrade with an Immutable Source, Unified Config-First Auth, Edge Route Coverage, and Migration Observability | Architecture | Closed | 2026-06-30 |
|
|
195
195
|
| [RDR-177](rdr-177-tenant-telemetry-usage-metering.md) | Unified Tenant-Scoped Telemetry, Observability, and Usage Metering for the Engine Service: One Stats Layer for Local Users and Cloud Tenants | Architecture | Draft | 2026-07-01 |
|
|
196
196
|
| [RDR-178](rdr-178-unattended-migration.md) | Unattended Migration: Industrialize the Store-Migration Path from Babysat to Autonomous | Architecture | Closed | 2026-07-01 |
|
|
197
|
+
| [RDR-179](rdr-179-self-correction-machinery-remediation.md) | Self-Correction Machinery Remediation: Relight Plan-Reuse, Retrieval Benchmarking, Plan-Library Hygiene, and Taxonomy-Aware Recall | Architecture | Draft | 2026-07-04 |
|
|
198
|
+
| [RDR-181](rdr-181-server-side-embed-skip-on-reindex.md) | Server-Side Embed-Skip on Re-Index: Stop Re-Embedding Chunks Whose Vector the Store Already Holds | Technical Debt | Accepted | 2026-07-05 |
|
|
197
199
|
|
|
198
200
|
> **Scrapped 2026-05-19 (RDR-110-119 arc).** Bundled the storage-substrate split with new abstractions (tuplespace, ORB, host-trust, surfaces-as-tuples, UI fabric); scope discipline failed across nine RDRs and 67 stranded beads. Files preserved as tombstones per the "never delete RDR files" rule. Postmortem: [docs/postmortem/2026-05-16-rdr110-113-remediation-chain.md](../postmortem/2026-05-16-rdr110-113-remediation-chain.md). Active substrate work continues as [RDR-120](rdr-120-storage-substrate-split.md) with an explicit moratorium on co-shipped consumers. Numbers RDR-114 through RDR-117 are unused on `main` (drafted on feature branches that never merged).
|
|
199
201
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "conexus-mcpb"
|
|
3
|
-
version = "6.3.
|
|
3
|
+
version = "6.3.2"
|
|
4
4
|
description = "Conexus packaged as Claude Desktop .mcpb (Desktop Extension)"
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
dependencies = [
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
# while collections are indexed at 768/1024-dim, so every T3 search hits a
|
|
11
11
|
# dimension mismatch and returns zero results. The .mcpb cannot run the
|
|
12
12
|
# interactive `nx init` embedder choice, so it must pin [local] explicitly.
|
|
13
|
-
"conexus[local]>=6.3.
|
|
13
|
+
"conexus[local]>=6.3.2",
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
[tool.uv]
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "conexus"
|
|
7
|
-
version = "6.3.
|
|
7
|
+
version = "6.3.2"
|
|
8
8
|
description = "Self-hosted semantic search and knowledge management for LLM-driven development"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.12,<3.14"
|
{conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/CatalogRepository.java
RENAMED
|
@@ -450,6 +450,152 @@ public final class CatalogRepository {
|
|
|
450
450
|
});
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
/**
|
|
454
|
+
* Batch variant of {@link #registerDocument} (nexus-9dvqy, duoak.11 sink #2).
|
|
455
|
+
*
|
|
456
|
+
* <p>Registers N documents under one owner in a SINGLE transaction, returning
|
|
457
|
+
* their tumblers in INPUT ORDER. The per-doc {@code registerDocument} path pays
|
|
458
|
+
* one {@code SELECT next_seq FOR UPDATE} owner-row lock per call — 2,133 serial
|
|
459
|
+
* WAN round-trips on a --force index (333s / 16% of the duoak.11 gate wall), all
|
|
460
|
+
* queued on the SAME owner lock (so client concurrency cannot help). This claims
|
|
461
|
+
* the whole contiguous sequence block under ONE lock acquisition and inserts all
|
|
462
|
+
* new rows in one multi-row INSERT.
|
|
463
|
+
*
|
|
464
|
+
* <p>Idempotency matches the single-doc path exactly: a doc whose {@code source_uri}
|
|
465
|
+
* (then {@code file_path}) already maps to a LIVE (non-tombstoned) document returns
|
|
466
|
+
* that existing tumbler and does NOT consume a sequence number — so a re-registration
|
|
467
|
+
* leaves no seq gap. Only genuinely-new docs draw from the claimed block.
|
|
468
|
+
*
|
|
469
|
+
* <p>Caller (the HTTP handler) caps the batch under the PostgreSQL 32767 bind-param
|
|
470
|
+
* ceiling; with ~24 columns per row the safe cap is 1000 rows (24000 params).
|
|
471
|
+
*/
|
|
472
|
+
public java.util.List<String> registerDocumentMany(
|
|
473
|
+
String tenant, String ownerPrefix, java.util.List<java.util.Map<String, Object>> docs) {
|
|
474
|
+
if (TenantConstants.isWildcard(tenant)) {
|
|
475
|
+
throw new IllegalArgumentException(
|
|
476
|
+
"tenant '*' is a reserved sentinel and cannot own catalog entries");
|
|
477
|
+
}
|
|
478
|
+
if (docs == null || docs.isEmpty()) {
|
|
479
|
+
return java.util.List.of();
|
|
480
|
+
}
|
|
481
|
+
return tenantScope.withTenant(tenant, ctx -> {
|
|
482
|
+
// Ensure owner row exists (idempotent upsert, minimal fields) — once.
|
|
483
|
+
ctx.insertInto(CATALOG_OWNERS, CATALOG_OWNERS.TENANT_ID, CATALOG_OWNERS.TUMBLER_PREFIX, CATALOG_OWNERS.NAME, CATALOG_OWNERS.OWNER_TYPE,
|
|
484
|
+
CATALOG_OWNERS.REPO_HASH, CATALOG_OWNERS.DESCRIPTION, CATALOG_OWNERS.REPO_ROOT, CATALOG_OWNERS.HEAD_HASH, CATALOG_OWNERS.NEXT_SEQ)
|
|
485
|
+
.values(tenant, ownerPrefix,
|
|
486
|
+
s(docs.get(0), "owner_name", ownerPrefix),
|
|
487
|
+
s(docs.get(0), "owner_type", "repo"),
|
|
488
|
+
null, null, "", null, 0L)
|
|
489
|
+
.onConflict(CATALOG_OWNERS.TENANT_ID, CATALOG_OWNERS.TUMBLER_PREFIX)
|
|
490
|
+
.doNothing()
|
|
491
|
+
.execute();
|
|
492
|
+
|
|
493
|
+
// Batch idempotency: fetch existing LIVE docs for every source_uri and
|
|
494
|
+
// file_path in the batch in ONE query per direction, then join locally.
|
|
495
|
+
java.util.List<String> srcUris = new java.util.ArrayList<>();
|
|
496
|
+
java.util.List<String> filePaths = new java.util.ArrayList<>();
|
|
497
|
+
for (var d : docs) {
|
|
498
|
+
String su = s(d, "source_uri", "");
|
|
499
|
+
if (!su.isEmpty()) srcUris.add(su);
|
|
500
|
+
String fp = s(d, "file_path", "");
|
|
501
|
+
if (!fp.isEmpty()) filePaths.add(fp);
|
|
502
|
+
}
|
|
503
|
+
java.util.Map<String, String> tumblerBySrcUri = new java.util.HashMap<>();
|
|
504
|
+
if (!srcUris.isEmpty()) {
|
|
505
|
+
ctx.select(CATALOG_DOCUMENTS.SOURCE_URI, CATALOG_DOCUMENTS.TUMBLER)
|
|
506
|
+
.from(CATALOG_DOCUMENTS)
|
|
507
|
+
.where(CATALOG_DOCUMENTS.TENANT_ID.eq(tenant)
|
|
508
|
+
.and(CATALOG_DOCUMENTS.SOURCE_URI.in(srcUris))
|
|
509
|
+
.and(CATALOG_DOCUMENTS.DELETED_AT.isNull()))
|
|
510
|
+
.fetch()
|
|
511
|
+
.forEach(r -> tumblerBySrcUri.putIfAbsent(r.value1(), r.value2()));
|
|
512
|
+
}
|
|
513
|
+
java.util.Map<String, String> tumblerByFilePath = new java.util.HashMap<>();
|
|
514
|
+
if (!filePaths.isEmpty()) {
|
|
515
|
+
ctx.select(CATALOG_DOCUMENTS.FILE_PATH, CATALOG_DOCUMENTS.TUMBLER)
|
|
516
|
+
.from(CATALOG_DOCUMENTS)
|
|
517
|
+
.where(CATALOG_DOCUMENTS.TENANT_ID.eq(tenant)
|
|
518
|
+
.and(CATALOG_DOCUMENTS.FILE_PATH.in(filePaths))
|
|
519
|
+
.and(CATALOG_DOCUMENTS.TUMBLER.startsWith(ownerPrefix + "."))
|
|
520
|
+
.and(CATALOG_DOCUMENTS.DELETED_AT.isNull()))
|
|
521
|
+
.fetch()
|
|
522
|
+
.forEach(r -> tumblerByFilePath.putIfAbsent(r.value1(), r.value2()));
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Resolve each doc to an existing tumbler or mark it NEW (input order).
|
|
526
|
+
String[] out = new String[docs.size()];
|
|
527
|
+
java.util.List<Integer> newIdx = new java.util.ArrayList<>();
|
|
528
|
+
for (int i = 0; i < docs.size(); i++) {
|
|
529
|
+
var d = docs.get(i);
|
|
530
|
+
String su = s(d, "source_uri", "");
|
|
531
|
+
String fp = s(d, "file_path", "");
|
|
532
|
+
String existing = null;
|
|
533
|
+
if (!su.isEmpty()) existing = tumblerBySrcUri.get(su);
|
|
534
|
+
if (existing == null && !fp.isEmpty()) existing = tumblerByFilePath.get(fp);
|
|
535
|
+
if (existing != null) {
|
|
536
|
+
out[i] = existing;
|
|
537
|
+
} else {
|
|
538
|
+
newIdx.add(i);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (!newIdx.isEmpty()) {
|
|
543
|
+
// Claim a contiguous seq block under ONE FOR UPDATE lock.
|
|
544
|
+
long seq = ctx.select(CATALOG_OWNERS.NEXT_SEQ).from(CATALOG_OWNERS)
|
|
545
|
+
.where(CATALOG_OWNERS.TENANT_ID.eq(tenant).and(CATALOG_OWNERS.TUMBLER_PREFIX.eq(ownerPrefix)))
|
|
546
|
+
.forUpdate()
|
|
547
|
+
.fetchOne(CATALOG_OWNERS.NEXT_SEQ);
|
|
548
|
+
long cursor = seq;
|
|
549
|
+
|
|
550
|
+
var insert = ctx.insertInto(CATALOG_DOCUMENTS,
|
|
551
|
+
CATALOG_DOCUMENTS.TENANT_ID, CATALOG_DOCUMENTS.TUMBLER, CATALOG_DOCUMENTS.TITLE, CATALOG_DOCUMENTS.AUTHOR, CATALOG_DOCUMENTS.YEAR,
|
|
552
|
+
CATALOG_DOCUMENTS.CONTENT_TYPE, CATALOG_DOCUMENTS.FILE_PATH, CATALOG_DOCUMENTS.CORPUS, CATALOG_DOCUMENTS.PHYSICAL_COLLECTION, CATALOG_DOCUMENTS.CHUNK_COUNT,
|
|
553
|
+
CATALOG_DOCUMENTS.HEAD_HASH, CATALOG_DOCUMENTS.INDEXED_AT, F_DOC_META, CATALOG_DOCUMENTS.SOURCE_MTIME, CATALOG_DOCUMENTS.ALIAS_OF, CATALOG_DOCUMENTS.SOURCE_URI,
|
|
554
|
+
CATALOG_DOCUMENTS.BIB_YEAR, CATALOG_DOCUMENTS.BIB_AUTHORS, CATALOG_DOCUMENTS.BIB_VENUE, CATALOG_DOCUMENTS.BIB_CITATION_COUNT,
|
|
555
|
+
CATALOG_DOCUMENTS.BIB_SEMANTIC_SCHOLAR_ID, CATALOG_DOCUMENTS.BIB_OPENALEX_ID, CATALOG_DOCUMENTS.BIB_DOI, CATALOG_DOCUMENTS.BIB_ENRICHED_AT);
|
|
556
|
+
for (int idx : newIdx) {
|
|
557
|
+
var fields = docs.get(idx);
|
|
558
|
+
cursor += 1;
|
|
559
|
+
String tumbler = ownerPrefix + "." + cursor;
|
|
560
|
+
out[idx] = tumbler;
|
|
561
|
+
String metaJson = jsonOrNull(fields.get("meta"));
|
|
562
|
+
insert = insert.values(tenant, tumbler,
|
|
563
|
+
s(fields, "title", ""),
|
|
564
|
+
nne(s(fields, "author", null)),
|
|
565
|
+
ni(i(fields, "year"), 0),
|
|
566
|
+
nne(s(fields, "content_type", "")),
|
|
567
|
+
nne(s(fields, "file_path", "")),
|
|
568
|
+
nne(s(fields, "corpus", "")),
|
|
569
|
+
nne(s(fields, "physical_collection", "")),
|
|
570
|
+
ni(i(fields, "chunk_count"), 0),
|
|
571
|
+
nne(s(fields, "head_hash", "")),
|
|
572
|
+
nne(s(fields, "indexed_at", "")),
|
|
573
|
+
jsonbVal(metaJson),
|
|
574
|
+
nd(dbl(fields, "source_mtime")),
|
|
575
|
+
nne(s(fields, "alias_of", "")),
|
|
576
|
+
nne(s(fields, "source_uri", "")),
|
|
577
|
+
ni(i(fields, "bib_year"), 0),
|
|
578
|
+
nne(s(fields, "bib_authors", "")),
|
|
579
|
+
nne(s(fields, "bib_venue", "")),
|
|
580
|
+
ni(i(fields, "bib_citation_count"), 0),
|
|
581
|
+
nne(s(fields, "bib_semantic_scholar_id", "")),
|
|
582
|
+
nne(s(fields, "bib_openalex_id", "")),
|
|
583
|
+
nne(s(fields, "bib_doi", "")),
|
|
584
|
+
nne(s(fields, "bib_enriched_at", "")));
|
|
585
|
+
}
|
|
586
|
+
insert.execute();
|
|
587
|
+
|
|
588
|
+
// Advance next_seq by exactly the number of new docs claimed.
|
|
589
|
+
ctx.update(CATALOG_OWNERS)
|
|
590
|
+
.set(CATALOG_OWNERS.NEXT_SEQ, cursor)
|
|
591
|
+
.where(CATALOG_OWNERS.TENANT_ID.eq(tenant).and(CATALOG_OWNERS.TUMBLER_PREFIX.eq(ownerPrefix)))
|
|
592
|
+
.execute();
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return java.util.Arrays.asList(out);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
453
599
|
/** Fetch a document by tumbler. Returns null if not found. */
|
|
454
600
|
public Map<String, Object> getDocument(String tenant, String tumbler) {
|
|
455
601
|
return tenantScope.withTenant(tenant, ctx -> {
|
|
@@ -1063,19 +1209,86 @@ public final class CatalogRepository {
|
|
|
1063
1209
|
/** Replace manifest for docId with the provided rows (atomic delete + insert). */
|
|
1064
1210
|
public void writeManifest(String tenant, String docId, List<Map<String, Object>> rows) {
|
|
1065
1211
|
tenantScope.withTenant(tenant, ctx -> {
|
|
1066
|
-
ctx
|
|
1067
|
-
for (var row : rows) {
|
|
1068
|
-
ctx.insertInto(CATALOG_DOCUMENT_CHUNKS,
|
|
1069
|
-
CATALOG_DOCUMENT_CHUNKS.TENANT_ID, CATALOG_DOCUMENT_CHUNKS.DOC_ID, CATALOG_DOCUMENT_CHUNKS.POSITION, CATALOG_DOCUMENT_CHUNKS.CHASH, CATALOG_DOCUMENT_CHUNKS.CHUNK_INDEX,
|
|
1070
|
-
CATALOG_DOCUMENT_CHUNKS.LINE_START, CATALOG_DOCUMENT_CHUNKS.LINE_END, CATALOG_DOCUMENT_CHUNKS.CHAR_START, CATALOG_DOCUMENT_CHUNKS.CHAR_END)
|
|
1071
|
-
.values(tenant, docId, i(row,"position"), s(row,"chash"), i(row,"chunk_index"),
|
|
1072
|
-
i(row,"line_start"), i(row,"line_end"), i(row,"char_start"), i(row,"char_end"))
|
|
1073
|
-
.execute();
|
|
1074
|
-
}
|
|
1212
|
+
writeManifestRows(ctx, tenant, docId, rows);
|
|
1075
1213
|
return null;
|
|
1076
1214
|
});
|
|
1077
1215
|
}
|
|
1078
1216
|
|
|
1217
|
+
/**
|
|
1218
|
+
* Shared REPLACE body (delete all rows for docId, then insert the provided
|
|
1219
|
+
* rows) used by both {@link #writeManifest} (one doc per transaction) and
|
|
1220
|
+
* {@link #writeManifestMany} (N docs, one transaction each). Assumes
|
|
1221
|
+
* {@code ctx} is already scoped to {@code tenant}. Does NOT touch
|
|
1222
|
+
* documents.chunk_count — {@code writeManifest}'s public behavior is unchanged;
|
|
1223
|
+
* the chunk_count fold-in is {@code writeManifestMany}'s addition.
|
|
1224
|
+
*/
|
|
1225
|
+
private static void writeManifestRows(DSLContext ctx, String tenant, String docId,
|
|
1226
|
+
List<Map<String, Object>> rows) {
|
|
1227
|
+
ctx.deleteFrom(CATALOG_DOCUMENT_CHUNKS).where(CATALOG_DOCUMENT_CHUNKS.DOC_ID.eq(docId)).execute();
|
|
1228
|
+
for (var row : rows) {
|
|
1229
|
+
ctx.insertInto(CATALOG_DOCUMENT_CHUNKS,
|
|
1230
|
+
CATALOG_DOCUMENT_CHUNKS.TENANT_ID, CATALOG_DOCUMENT_CHUNKS.DOC_ID, CATALOG_DOCUMENT_CHUNKS.POSITION, CATALOG_DOCUMENT_CHUNKS.CHASH, CATALOG_DOCUMENT_CHUNKS.CHUNK_INDEX,
|
|
1231
|
+
CATALOG_DOCUMENT_CHUNKS.LINE_START, CATALOG_DOCUMENT_CHUNKS.LINE_END, CATALOG_DOCUMENT_CHUNKS.CHAR_START, CATALOG_DOCUMENT_CHUNKS.CHAR_END)
|
|
1232
|
+
.values(tenant, docId, i(row,"position"), s(row,"chash"), i(row,"chunk_index"),
|
|
1233
|
+
i(row,"line_start"), i(row,"line_end"), i(row,"char_start"), i(row,"char_end"))
|
|
1234
|
+
.execute();
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Batch REPLACE manifest for multiple docs (bead nexus-u2kwq). Each doc is
|
|
1240
|
+
* replaced in its OWN {@link TenantScope#withTenant} transaction that folds
|
|
1241
|
+
* together the {@link #writeManifest} REPLACE (delete all rows + insert) AND
|
|
1242
|
+
* a {@code documents.chunk_count = rows.size()} UPDATE — collapsing the
|
|
1243
|
+
* client's separate {@code /manifest/write} + chunk_count {@code /update}
|
|
1244
|
+
* round-trips into one atomic per-doc write. A failure on one doc rolls back
|
|
1245
|
+
* only that doc and its doc_id is collected in {@code failed_doc_ids}; sibling
|
|
1246
|
+
* docs are unaffected (per-doc atomicity, cross-doc isolation). Empty list is
|
|
1247
|
+
* a no-op.
|
|
1248
|
+
*
|
|
1249
|
+
* @param docs each entry {@code {"doc_id": "...", "rows": [<manifest row>...]}}.
|
|
1250
|
+
* @return {@code {docs: <int ok>, rows: <int total written>, failed_doc_ids: [...]}}.
|
|
1251
|
+
*/
|
|
1252
|
+
public Map<String, Object> writeManifestMany(String tenant, List<Map<String, Object>> docs) {
|
|
1253
|
+
int okDocs = 0;
|
|
1254
|
+
int totalRows = 0;
|
|
1255
|
+
List<String> failed = new ArrayList<>();
|
|
1256
|
+
if (docs != null) {
|
|
1257
|
+
for (Map<String, Object> d : docs) {
|
|
1258
|
+
String docId = s(d, "doc_id");
|
|
1259
|
+
Object rawRows = d.get("rows");
|
|
1260
|
+
@SuppressWarnings("unchecked")
|
|
1261
|
+
List<Map<String, Object>> rows = rawRows instanceof List<?> l
|
|
1262
|
+
? (List<Map<String, Object>>) l
|
|
1263
|
+
: List.of();
|
|
1264
|
+
try {
|
|
1265
|
+
if (docId == null || docId.isBlank()) {
|
|
1266
|
+
throw new IllegalArgumentException("'doc_id' required");
|
|
1267
|
+
}
|
|
1268
|
+
tenantScope.withTenant(tenant, ctx -> {
|
|
1269
|
+
writeManifestRows(ctx, tenant, docId, rows);
|
|
1270
|
+
ctx.update(CATALOG_DOCUMENTS)
|
|
1271
|
+
.set(CATALOG_DOCUMENTS.CHUNK_COUNT, rows.size())
|
|
1272
|
+
.where(CATALOG_DOCUMENTS.TUMBLER.eq(docId))
|
|
1273
|
+
.execute();
|
|
1274
|
+
return null;
|
|
1275
|
+
});
|
|
1276
|
+
okDocs++;
|
|
1277
|
+
totalRows += rows.size();
|
|
1278
|
+
} catch (Exception e) {
|
|
1279
|
+
log.debug("event=write_manifest_many_doc_failed tenant={} doc_id={} error={}",
|
|
1280
|
+
tenant, docId, e.getMessage());
|
|
1281
|
+
failed.add(docId);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
Map<String, Object> result = new LinkedHashMap<>();
|
|
1286
|
+
result.put("docs", okDocs);
|
|
1287
|
+
result.put("rows", totalRows);
|
|
1288
|
+
result.put("failed_doc_ids", failed);
|
|
1289
|
+
return result;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1079
1292
|
/** Append manifest rows (upsert by position). */
|
|
1080
1293
|
public void appendManifestChunks(String tenant, String docId, List<Map<String, Object>> rows) {
|
|
1081
1294
|
tenantScope.withTenant(tenant, ctx -> {
|
{conexus-6.3.1 → conexus-6.3.2}/service/src/main/java/dev/nexus/service/db/ChashRepository.java
RENAMED
|
@@ -173,15 +173,35 @@ public final class ChashRepository {
|
|
|
173
173
|
if (chashes == null || chashes.isEmpty()) return;
|
|
174
174
|
|
|
175
175
|
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
|
|
176
|
+
// distinct(): a multi-VALUES INSERT .. ON CONFLICT DO UPDATE raises
|
|
177
|
+
// "cannot affect row a second time" (-> HTTP 500) when the same chash
|
|
178
|
+
// appears twice in one statement, and real files emit duplicate chunk
|
|
179
|
+
// text (nexus-85z0y). Dedup is semantics-free: a chash is a content
|
|
180
|
+
// hash, so every occurrence is identical.
|
|
181
|
+
// sorted(): the multi-row INSERT locks CHASH_INDEX rows in values() order.
|
|
182
|
+
// tenant + collection are constant within this call, so chash is the only
|
|
183
|
+
// varying part of the (tenant, chash, physical_collection) conflict key —
|
|
184
|
+
// sorting by chash gives every concurrent batch one global lock order and
|
|
185
|
+
// removes the cross-batch deadlock (SQLSTATE 40P01, nexus-ps9wb; same class as
|
|
186
|
+
// PgVectorRepository.upsertChunks). distinct(): a multi-VALUES INSERT ..
|
|
187
|
+
// ON CONFLICT DO UPDATE raises "cannot affect row a second time" (-> HTTP 500)
|
|
188
|
+
// when the same chash appears twice in one statement, and real files emit
|
|
189
|
+
// duplicate chunk text (nexus-85z0y). Dedup is semantics-free: a chash is a
|
|
190
|
+
// content hash, so every occurrence is identical.
|
|
176
191
|
List<String> valid = chashes.stream()
|
|
177
192
|
.filter(c -> c != null && !c.isBlank())
|
|
193
|
+
.distinct()
|
|
194
|
+
.sorted()
|
|
178
195
|
.toList();
|
|
179
196
|
if (valid.isEmpty()) return;
|
|
180
197
|
|
|
181
198
|
// Own short committed txn — bounds the first-registration convoy DURATION
|
|
182
199
|
// (see registerCollectionShortTxn doc); also handles markKnown post-commit.
|
|
183
200
|
registerCollectionShortTxn(tenant, collection);
|
|
184
|
-
|
|
201
|
+
// nexus-ps9wb belt: retry a residual cross-path deadlock (the sort removes the
|
|
202
|
+
// same-batch cycle; a concurrent writer on a different lock order can still
|
|
203
|
+
// deadlock). Idempotent ON CONFLICT batch, victim already rolled back → safe.
|
|
204
|
+
DeadlockRetry.run(collection, () -> tenantScope.withTenant(tenant, ctx -> {
|
|
185
205
|
var insert = ctx.insertInto(CHASH_INDEX,
|
|
186
206
|
CHASH_INDEX.TENANT_ID, CHASH_INDEX.CHASH, CHASH_INDEX.PHYSICAL_COLLECTION, CHASH_INDEX.CREATED_AT);
|
|
187
207
|
var step = insert.values(tenant, valid.get(0), collection, now);
|
|
@@ -193,7 +213,7 @@ public final class ChashRepository {
|
|
|
193
213
|
.set(CHASH_INDEX.CREATED_AT, DSL.field("EXCLUDED.created_at", OffsetDateTime.class))
|
|
194
214
|
.execute();
|
|
195
215
|
return null;
|
|
196
|
-
});
|
|
216
|
+
}));
|
|
197
217
|
}
|
|
198
218
|
|
|
199
219
|
// ── lookup ─────────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 Hal Hildebrand. All rights reserved.
|
|
3
|
+
package dev.nexus.service.db;
|
|
4
|
+
|
|
5
|
+
import org.slf4j.Logger;
|
|
6
|
+
import org.slf4j.LoggerFactory;
|
|
7
|
+
|
|
8
|
+
import java.sql.SQLException;
|
|
9
|
+
import java.util.concurrent.ThreadLocalRandom;
|
|
10
|
+
import java.util.function.Supplier;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Shared deadlock-retry belt for the service's multi-row
|
|
14
|
+
* {@code INSERT ... ON CONFLICT (...) DO UPDATE} write paths (bead nexus-ps9wb).
|
|
15
|
+
*
|
|
16
|
+
* <p><strong>Background.</strong> Two concurrent multi-row upsert batches into the
|
|
17
|
+
* same table that touch an overlapping set of conflict keys in DIFFERENT arrival
|
|
18
|
+
* orders lock the shared rows in opposite orders within their single statement →
|
|
19
|
+
* lock cycle → Postgres kills a victim with {@code deadlock detected} (SQLSTATE
|
|
20
|
+
* 40P01) → the caller sees HTTP 500. The PRIMARY fix at each call site is to sort a
|
|
21
|
+
* batch's rows by the ON CONFLICT key before binding, so every concurrent batch
|
|
22
|
+
* acquires row locks in one global order and no cycle can form. This helper is the
|
|
23
|
+
* BELT for residual deadlocks the per-site sort cannot rule out — a concurrent
|
|
24
|
+
* delete, or a different repository writing an overlapping keyspace in its own order.
|
|
25
|
+
*
|
|
26
|
+
* <p><strong>Safety.</strong> The deadlock victim's transaction is ALREADY rolled
|
|
27
|
+
* back by Postgres before the exception surfaces, so re-running an idempotent
|
|
28
|
+
* {@code ON CONFLICT DO UPDATE} batch is safe (no partial state, no double effect).
|
|
29
|
+
* Attempts are bounded ({@link #MAX_ATTEMPTS}) with jittered backoff; the original
|
|
30
|
+
* exception propagates unchanged on exhaustion or for any non-deadlock error. The
|
|
31
|
+
* wrapped unit MUST be only the DB transaction — never anything with external side
|
|
32
|
+
* effects (e.g. embedding calls), or a retry would repeat them.
|
|
33
|
+
*/
|
|
34
|
+
public final class DeadlockRetry {
|
|
35
|
+
|
|
36
|
+
private static final Logger log = LoggerFactory.getLogger(DeadlockRetry.class);
|
|
37
|
+
|
|
38
|
+
/** Total attempts (initial try + retries) before the deadlock propagates. */
|
|
39
|
+
static final int MAX_ATTEMPTS = 4;
|
|
40
|
+
|
|
41
|
+
/** PostgreSQL SQLSTATE for {@code deadlock_detected}. */
|
|
42
|
+
static final String SQLSTATE_DEADLOCK = "40P01";
|
|
43
|
+
|
|
44
|
+
private DeadlockRetry() {
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Run {@code writeTxn} (a single idempotent DB transaction), retrying on a
|
|
49
|
+
* deadlock (SQLSTATE 40P01) up to {@link #MAX_ATTEMPTS} total attempts.
|
|
50
|
+
*
|
|
51
|
+
* @param context short label for the retry log line (e.g. the collection name)
|
|
52
|
+
* @param writeTxn the transaction to run; MUST be free of external side effects
|
|
53
|
+
*/
|
|
54
|
+
public static void run(String context, Runnable writeTxn) {
|
|
55
|
+
run(context, () -> {
|
|
56
|
+
writeTxn.run();
|
|
57
|
+
return null;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* {@link #run(String, Runnable)} variant returning the transaction's value.
|
|
63
|
+
*/
|
|
64
|
+
public static <T> T run(String context, Supplier<T> writeTxn) {
|
|
65
|
+
int attempt = 0;
|
|
66
|
+
while (true) {
|
|
67
|
+
try {
|
|
68
|
+
return writeTxn.get();
|
|
69
|
+
} catch (RuntimeException ex) {
|
|
70
|
+
if (isDeadlock(ex) && ++attempt < MAX_ATTEMPTS) {
|
|
71
|
+
log.warn("event=deadlock_retry context={} attempt={} maxAttempts={}",
|
|
72
|
+
context, attempt, MAX_ATTEMPTS);
|
|
73
|
+
backoff(attempt);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
throw ex;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* True if a {@code deadlock_detected} (SQLSTATE 40P01) SQLException appears
|
|
83
|
+
* anywhere in {@code t}'s cause chain. jOOQ wraps the driver SQLException in a
|
|
84
|
+
* {@code DataAccessException} (and TenantScope may further wrap that in a
|
|
85
|
+
* RuntimeException), so the SQLException is a cause, not the top-level throwable —
|
|
86
|
+
* hence the depth-bounded chain walk, which handles either wrapper.
|
|
87
|
+
*/
|
|
88
|
+
static boolean isDeadlock(Throwable t) {
|
|
89
|
+
Throwable c = t;
|
|
90
|
+
for (int depth = 0; c != null && depth < 32; depth++, c = c.getCause()) {
|
|
91
|
+
if (c instanceof SQLException se && SQLSTATE_DEADLOCK.equals(se.getSQLState())) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private static void backoff(int attempt) {
|
|
99
|
+
long ms = Math.min(attempt, MAX_ATTEMPTS) * (10L + ThreadLocalRandom.current().nextLong(20));
|
|
100
|
+
try {
|
|
101
|
+
Thread.sleep(ms);
|
|
102
|
+
} catch (InterruptedException ie) {
|
|
103
|
+
Thread.currentThread().interrupt();
|
|
104
|
+
throw new RuntimeException("interrupted during deadlock-retry backoff", ie);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|