Skip to content

Commit efc8d00

Browse files
Jeadielukekimclaudespicesgrebnovphillipleblanc
authored
fix(tests): stabilize flaky SQL search snapshots with score normalization (spiceai#10585)
* update snapshots * check but redact scores * fix: use to_string_pretty in assert_search_response_snapshot * fix check * clippy * fix: use expect instead of unwrap_or_default in snapshot assert * ci: replace curl|sh ClickHouse install with pinned v26.3.9.8 packages * fix: round scores to 2dp before sort to restore stable snapshot ordering * fix: round scores to 2dp in snapshots instead of fully redacting * formatting * formatting * snapshots * snapshots * clippy * fix: extend structural validation to all s3vectors HTTP search tests S3 Vectors is a remote ANN backend — the specific items it returns can change between runs (not just their ordering). The 'composite' exemption was too narrow; extend it to all s3vectors tests except those using direct SQL (vector_search_sql) or with_where filters, which test specific deterministic behaviour. Also update snapshots for: - multi_embedding_parent_child_basic: was truncated, now has full content - multi_column_view_answer_basic: new test, initial snapshot - s3vectors_chunking_view_with_where: id:551 now ranks 3rd (ahead of id:1035) - text_search_metadata_sql: id:494 before id:495 (primary-key tie-break), new row id:1175 added to dataset * fix and clippy * fix: check both primary_key and data for additional_columns assertion For s3_vectors, additional_columns are returned in the `data` field rather than `primary_key`. The assertion now accepts either location. * fix: restore outer JSON structure in multi_column_view_answer_keywords snapshot * fix: redact and stably sort _score in SQL search snapshots The SearchTestType::Sql branch was snapshotting raw `/v1/sql` JSON, so non-deterministic embedding scores (s3vectors and similar) caused snapshots like s3vectors_chunking_view_vector_search_sql_filters to flap when two items rounded to the same 2dp _score but their underlying floats sorted differently across CI runners. Add `normalize_sql_response_json` mirroring the existing HTTP-side normalization: sort by `_score` rounded to 2dp descending with a content-based tiebreak (BTreeMap-serialized row, _score excluded), then replace each row's `_score` with `"[score]"`. No-op for SQL responses that don't include `_score` (e.g. `SELECT subject FROM ...`). Updates the 54 affected SQL snapshot files accordingly. * fix: resolve trunk merge conflict in search.rs The merge from trunk (commit 9d48891aa) reintroduced a 3-arg `assert_search_response_structure(name, resp, round_scores)` call site and 3-arg test invocations that don't match this PR's 2-arg signature (`fn assert_search_response_structure(test_name: &str, resp: &Value)`), causing E0425 (`round_scores` undefined) and three E0061 errors. Align all call sites to the PR's signature: pass `&resp` at line 307 and drop the `round_scores: bool` arguments from the two structural test functions. Also addresses Copilot review feedback: replace `unwrap_or_default()` with `expect()` in `normalize_search_response` and `row_tiebreak_key` so serialization failures fail loudly instead of silently producing empty strings. * fix(tests): extend structural validation to all s3vectors_* HTTP search tests CI run https://github.com/spiceai/spiceai/actions/runs/25622131180 failed on five HTTP snapshot tests that were not previously covered by the structural validation matcher: - s3vectors_basic_additional_columns - s3vectors_metadata_additional_columns - s3vectors_hybrid_view_basic - s3vectors_multiple_embeddings_basic These tests round _score to 2 decimal places and tie-break by content, but when actual similarity scores vary by ±0.01 across CI runner hardware and straddle a rounding boundary, the rounded ranks differ between runs and ordering flips - producing snapshot diffs even though the underlying result set is unchanged. Broaden the matcher so any s3vectors_* HTTP search test (not just composite and chunking) goes through structural validation. with_where variants remain on the snapshot path for non-chunking categories so their deterministic filter expectations are still asserted; chunking with_where keeps its previous structural-validation behavior to avoid regressing tests that already passed under the old matcher. vector_search_sql tests continue to use the normalize_sql_response_json path and are unchanged. * fix(tests): stabilize flaky s3vectors chunking SQL filter snapshots (#10851) * fix(mssql): Push topK limit to SQL Server for non-nullable sort columns (#10621) * fix(mssql): return Exact sort pushdown for non-nullable sort columns If a sort column is not nullable, NULLS FIRST/LAST ordering is irrelevant (there are no NULLs to order), so try_pushdown_sort can return Exact unconditionally for those columns. This allows limit pushdown to propagate through to SqlServerExec for the common case of sorting on non-nullable columns (e.g. primary keys), producing TOP N in the emitted SQL. * fix(mssql): respect IS_NULLABLE from INFORMATION_SCHEMA when building schema Previously every column was hardcoded as nullable (Field::new(..., true)), which meant the non-nullable Exact sort pushdown path added in the previous commit could never be exercised. Now IS_NULLABLE is fetched alongside the other column metadata and used to set the correct nullability on each field. * Fix formatting * PR comments --------- Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> * TLS cert hot-reload (mTLS plan M1) (#10727) Centralize CertWatcher behind `runtime::tls::TlsControl`, atomic cluster mTLS bundle swaps via `ClusterPkiBundle`, and address review: - bind_cluster_listener uses UnableToBindClusterListener. - ClusterPkiBundle exposes root_hint_subjects() captured once at construction (cert/key rotations preserve it; CA replacement is rare and requires restart for hint refresh; verification still uses the current snapshot). - trigger_reload_all docs reflect best-effort async dispatch. - SIGHUP handler docs match: `reload_all` enqueues, dispatcher thread runs callbacks, observe `tls_reload_total` for confirmation. - Drop dead ReloadableClientVerifier (superseded by ClusterPkiBundle). * fix(snapshot): keep refresh_mode snapshot read-only (#10752) * fix: Update benchmark snapshots May-8 (#10746) * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-cayenne[file]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[file]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[memory]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/postgres-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-duckdb[file]-partitioned.yaml * fix: Update tpch benchmark snapshots for accelerated/mongodb-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-duckdb[file]-2gib.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-cayenne[file].yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/mysql-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-cayenne[file]-partitioned.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow-partitioned.yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-arrow-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-cayenne[file]-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-duckdb[file]-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/dynamodb-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-sqlite[file].yaml * fix: Update tpch benchmark snapshots for accelerated/iceberg-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/databricks[delta_lake]-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-cayenne[file]turso.yaml * fix: Update tpch benchmark snapshots for accelerated/mongodb-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-duckdb[file]-4gib.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/mysql-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/spicecloud-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/postgres-arrow.yaml * fix: Update tpch benchmark snapshots for federated/abfs_standard_versioned[parquet].yaml * fix: Update tpch[parameterized] benchmark snapshots for federated/file[parquet].yaml * fix: Update tpch benchmark snapshots for federated/iceberg[catalog].yaml * fix: Update tpch benchmark snapshots for federated/file[parquet].yaml * fix: Update tpch benchmark snapshots for federated/glue[catalog].yaml * fix: Update tpch benchmark snapshots for federated/glue[parquet].yaml * fix: Update tpch benchmark snapshots for federated/mongodb.yaml * fix(benchmarks): Limit sf100 Arrow in-memory rows to prevent OOM (#10730) * fix(benchmarks): Limit sf100 Arrow in-memory rows to prevent OOM * Update * Update * Update * Set memory_limit * Update * Update * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow.yaml --------- Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix: Update tpch benchmark snapshots for federated/abfs[parquet].yaml * fix: Update tpch benchmark snapshots for federated/databricks[delta_lake].yaml * fix: Update tpch benchmark snapshots for federated/mssql.yaml * fix: Update tpch benchmark snapshots for federated/glue[csv].yaml * fix: Update tpch benchmark snapshots for federated/spicecloud[catalog].yaml * fix: Update tpch[parameterized] benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpch benchmark snapshots for federated/databricks[catalog].yaml * fix: Update tpch benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpch benchmark snapshots for federated/oracle[catalog].yaml * fix: Update tpch benchmark snapshots for federated/snowflake[catalog].yaml * fix: Update tpch benchmark snapshots for federated/iceberg[hadoop].yaml * fix: Update tpch benchmark snapshots for federated/mssql[catalog].yaml * fix: Update tpch benchmark snapshots for federated/oracle.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-cayenne[file]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[file]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[memory]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/postgres-duckdb[file].yaml * fix: Update tpch benchmark snapshots for federated/dynamodb.yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-duckdb[memory].yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-cayenne[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/spicecloud-duckdb[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-duckdb[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/mysql-duckdb[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-arrow-partitioned.yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-sqlite[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/databricks[delta_lake]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/databricks[delta_lake]-duckdb[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-duckdb[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-duckdb[memory].yaml * fix: Update tpcds benchmark snapshots for accelerated/mysql-duckdb[memory].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-postgres.yaml * fix: Update tpcds benchmark snapshots for accelerated/spicecloud-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/postgres-arrow.yaml * fix: Update tpcds benchmark snapshots for federated/duckdb.yaml * fix: Update tpcds benchmark snapshots for federated/file[parquet].yaml * fix: Update tpcds benchmark snapshots for federated/postgres.yaml * fix: Update tpcds benchmark snapshots for federated/spicecloud.yaml * fix: Update tpcds benchmark snapshots for federated/abfs[parquet].yaml * fix: Update tpcds benchmark snapshots for federated/dremio.yaml * fix: Update tpcds benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpcds benchmark snapshots for federated/mysql.yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-arrow-partitioned.yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-sqlite[file].yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-duckdb[file].yaml * fix: Update clickbench benchmark snapshots for accelerated/spicecloud-duckdb[file].yaml * fix: Update clickbench benchmark snapshots for federated/s3[parquet].yaml * fix: Update clickbench benchmark snapshots for accelerated/spicecloud-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-duckdb[file]-64gib.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * Trigger CI * Trigger CI * fix: Update tpcds benchmark snapshots for federated/databricks[delta_lake].yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-postgres.yaml * fix: Update tpch benchmark snapshots for accelerated/dynamodb-duckdb[file].yaml * Trigger CI * Trigger CI --------- Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Persist HTTP rate-control state in object storage (#10697) * feat: persist HTTP rate control state * feat: add snapshot method and test for persisted rate control state Co-authored-by: Copilot <copilot@github.com> * fix: change error source type to Box for persistence errors * feat: add support for globally persisted HTTP rate-control state and update documentation * feat: implement refresh gate in RateControllerPersistence and update object store prefix handling for abfs schemes Co-authored-by: Copilot <copilot@github.com> * feat: enhance rate control persistence and improve documentation for state location * test: cover persisted rate-control state recreation * ci: authenticate to Docker Hub in workflows that pull images (#10707) Add a Docker Hub login step (using DOCKERHUB_USERNAME / DOCKERHUB_TOKEN) to every workflow that pulls images from Docker Hub, so all pulls use the authenticated rate limits instead of anonymous (per-IP) limits. Covers: - Direct `docker pull` of postgres / mysql in the four bench workflows. - `docker run registry:2` (Docker Hub) in spiced_docker.yml, spiced_docker_dev.yml, and build_nightly.yml. - `docker/build-push-action` jobs whose Dockerfiles `FROM` Docker Hub bases (debian, rust, ubuntu, nvidia/cuda) in spiced_docker.yml (amd64/arm64/cuda), spiced_docker_dev.yml, build_nightly.yml, spidapter_build_and_release.yml, and testoperator_build_and_release.yml. Each new step is gated on `github.repository == 'spiceai/spiceai'` so it is a no-op on forks (which don't have access to the secrets). * Update openapi.json (#10701) Co-authored-by: Spice Schema Bot <schema-bot@spice.ai> Co-authored-by: Luke Kim <80174+lukekim@users.noreply.github.com> * feat: add tempfile as a dev-dependency and improve rate control state handling * feat: enhance rate control persistence with background tasks and file state support Co-authored-by: Copilot <copilot@github.com> * feat: integrate rate control into Databricks and Spark connectors - Added RateController support to DatabricksSparkConnect and SparkConnect. - Implemented new methods for creating instances with optional rate controllers. - Enhanced DatabricksSqlWarehouse to utilize rate control for SQL execution. - Updated UnityCatalog to include rate control permits in HTTP requests. - Introduced shared rate controller configuration for Databricks catalog. - Modified HTTP rate control parameters for better integration with Databricks. - Added tests to ensure proper rate limiting behavior during SQL statement execution. * refactor: simplify endpoint handling in UnityCatalog initialization * feat: implement source rate control for GitHub connector and update related configurations * feat: implement source rate control in runtime and update related schema and tests * fix: correct variable naming in rate control refresh interval parsing * fix: correct typo in features variable assignment in integration workflow Co-authored-by: Copilot <copilot@github.com> * fix: simplify runtime initialization in GitHub tests and improve JSON parsing * feat: enhance rate control with globally persisted state and instance management * refactor: remove unused refresh_persisted_governor_states method from HttpRateControlRegistry * refactor: update dependencies and improve concurrency control documentation * docs: improve formatting of supported object stores table in README * Lint * Lint * Clippy * feat(rate-control): coordinate and pace leases with persisted state --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Spice Schema Bot <schema-bot@spice.ai> Co-authored-by: Viktor Yershov <viktor@spice.ai> Co-authored-by: Phillip LeBlanc <879445+phillipleblanc@users.noreply.github.com> * refactor(elasticsearch): move write lifecycle hooks to TableSink via sink_indexes field (#10736) * Add Elasticsearch vector and FTS index wrapping to accelerator setup - EmbeddingConnector now wraps accelerators with Elasticsearch vector indexes when configured, enabling vector search support. - ElasticsearchFullTextConnector implements on_accelerator_setup to wrap accelerators with FTS index logic. - Refactored ElasticsearchTextIndex to move write maintenance logic into explicit on_write_start/on_write_failed/on_write_complete methods, ensuring correct lifecycle handling for index maintenance. * Add tests for ElasticsearchIndexWriteMaintenance behavior * fix: prevent accelerator-side ES indexes from being picked up by query optimizer * fix formatting * refactor: move ES sink indexes from IndexedTableProvider to TableSink * fix * fix formatting * fix extra arg * fix: forward sink_indexes through TableSink→MultiSink conversion and run write lifecycle hooks in MultiSink When add_synchronized_table converted a TableSink to a MultiSink, it only carried over the table_provider, silently dropping any sink_indexes (e.g. Elasticsearch indexes). MultiSink now holds a sink_indexes field, the conversion preserves them via std::mem::take, and insert_into runs on_write_start/on_write_complete/on_write_failed around the primary write. * fix elasticsearch error usage * fix formatting * keep comment * Refactor ES vector index creation for accelerator path Replaces wrap_accelerator_with_elasticsearch_vector_indexes with build_elasticsearch_sink_indexes, clarifying intent and avoiding unnecessary TableProvider wrapping. Updates EmbeddingConnector and read-side index wrapping to use the new function. No change to data correctness or index registration semantics. * Remove sink index registration from accelerator builder Elasticsearch and full-text indexes are now discovered from the federated provider chain and registered automatically. Removes the `add_sink_index` and `with_sink_indexes` APIs and all manual sink index registration logic from the accelerator and embedding connectors. This simplifies index lifecycle management and prevents accidental omission of write hooks for external indexes. * fix: walk full provider chain in indexes_from_federated * fix: remove unnecessary Arc::new wrapping of RefreshTask * fix: make indexes_from_federated future Send by extracting sync traversal * Update * Fix lint * fix: avoid blocking on deferred FederatedTable in RefreshTaskBuilder::build() `indexes_from_federated` called `table_provider().await` which blocks indefinitely when the provider is deferred due to schema mismatch, causing the schema_evolution integration test to timeout. The fix uses try_table_provider_sync() instead --------- Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> * perf(cdc): coalesce envelopes and overlap commits in apply pipeline (#10745) * perf(cdc): coalesce envelopes and overlap commits in apply pipeline The previous CDC pipelining attempt (#10676, reverted in #10709) added a per-envelope mpsc hop between source-stream reads and the apply loop, but without coalescing or commit overlap. Reads were already async, so the extra hop did not unlock real parallelism; the destination write and source-side commit (PG `Standby Status Update`, Kafka offset commit, DynamoDB shard checkpoint) still serialized on the apply path, and the mpsc plumbing alone added enough overhead to regress steady-state throughput. This change reworks the apply loop to actually win: * Coalesce contiguous `Ok` envelopes drained from the prefetch channel into a single accelerator write via `arrow::compute::concat_batches`. This amortizes per-envelope `SessionContext` + `insert_into` planning cost across the whole burst, which dominates on high-frequency CDC. * Overlap commit with the next apply by keeping at most one in-flight commit task and awaiting it before spawning the next one. This preserves strict commit ordering (LSN/offsets advance monotonically) while letting commit(N) overlap with apply(N+1) -- the actual idle window in the original serial loop. * Fast-path single envelope when the prefetch buffer is empty, so the no-burst case matches pre-pipelining cost exactly. Tunables are exposed via `runtime.params`: runtime: params: cdc_prefetch_buffer: "32" # 1..=1024, default 32 cdc_max_coalesced_envelopes: "64" # 1..=4096, default 64 Env-var fallback is preserved for tests and ad-hoc tuning (`SPICE_CDC_PREFETCH_BUFFER`, `SPICE_CDC_MAX_COALESCED_ENVELOPES`). Out-of-range or unparseable values fall back to defaults with a `tracing::warn!`. Tests ----- * All existing `accelerated_table::refresh_task::changes` unit tests pass (ordering, commit-after-write, error continuation, ready signaling, pipelining, reader cancellation), with the commit-after-write assertion updated to reflect the coalescing invariant (1 burst write + N commits, all commits after the write). * New end-to-end test `cdc_cayenne_inline` exercises the full pipeline through a Cayenne accelerator using the data-inlining write path: asserts every envelope's commit fires once, all rows land in the accelerator, and the Cayenne metastore reports rows stored inline. * New Criterion benchmark `cdc_cayenne_inline` reports envelopes/sec through the same path at N in {16, 64, 256, 1024}. * style: format code for consistency and readability in CDC-related files * refactor(refresh): encapsulate parameters in ApplyContext for apply_burst and apply_envelope_run methods * test(cdc): fix cayenne inline test clippy warnings * refactor(cdc): improve commit handling and logging in CDC pipeline tests Co-authored-by: Copilot <copilot@github.com> * fix(cdc): address review feedback on config and commits * Refactor PostgreSQL replication client to simplify relation resolution - Removed the `resolve_relation_with_declared_pks` function and replaced its usage with `resolve_relation`, which directly returns a borrowed relation. - Updated the WAL stream processing logic to utilize the new relation resolution method, ensuring that primary keys are applied correctly without unnecessary cloning. - Enhanced the `Decoder` struct to include a method for applying declared primary keys to cached relations, improving performance by avoiding repeated cloning. --- Improve CDC configuration handling in the runtime - Consolidated CDC tunable parameters into a single `CdcConfig` struct, adding support for `max_coalesced_bytes` and `commit_timeout`. - Updated the configuration loading logic to read new parameters from the environment and runtime parameters, ensuring defaults are applied correctly. - Enhanced the commit handling logic to support timeouts, improving robustness during shutdown scenarios. --- Optimize deletion expression building in the refresh task - Refactored the `build_batch_delete_expr_from_change_batch` function to handle cases with no primary keys more gracefully. - Introduced helper functions to streamline the creation of delete expressions from change batches, improving readability and maintainability. - Added checks to ensure that primary keys are defined before attempting to build delete expressions, reducing potential runtime errors. --- Refactor streaming data update execution plan - Changed the `record_batch_stream` field in `StreamingDataUpdateExecutionPlan` to hold an `Option`, allowing for better management of the stream's lifecycle. - Updated the execution logic to ensure that the stream is only consumed once, preventing potential runtime errors related to multiple consumptions. - Improved error handling during stream locking and consumption, enhancing the robustness of the execution plan. * refactor: optimize retention and sorting logic in CayenneDataSink and TursoDataSink * refactor(turso): remove unnecessary mutability from data parameter in write_all method * refactor(turso): enhance testing for TursoDataSink and improve batch handling logic refactor(runtime): encapsulate changes refresh logic for DuckDB in a dedicated function * refactor(tests): rename DuckDB test functions for clarity on recompute statistics behavior * fix(kafka): handle missing payload in Kafka messages and improve error reporting feat(runtime): enhance join_pending_commit to report panics during shutdown * feat(kafka): add #[must_use] attribute to from_borrowed_messages method * fix(sink): streamline inline batch processing and checkpointing logic * fix(runtime): gate test-only delete helpers * fix(cdc): preserve kafka payload parsing and failure tests * fix(cdc): stop stream after write failure --------- Co-authored-by: Copilot <copilot@github.com> * Improve cli table layout (#10725) * Improve cli table layout * Fix snapshots * Add `install-runtime-dev` to Makefile (#10718) * Add install-runtime-dev * Rename runtime->spiced in Makefile * Fix workflow --------- Co-authored-by: Luke Kim <80174+lukekim@users.noreply.github.com> * Add Snowflake DML support (#10747) * Add Snowflake DML support * fix: Refactor scalar_to_u64 and decimal_to_sql_literal for improved handling of types Co-authored-by: Copilot <copilot@github.com> * feat: Enhance Snowflake table path handling with quoting and validation * Add Snowflake read_write_provider and federated write support - Implement DataConnector::read_write_provider for Snowflake - Wrap SnowflakeTableProvider in FederatedTableProviderAdaptor for writes - Forward FederationProvider methods for SnowflakeTableProvider - Add tracing for SnowflakeTableProvider::insert_into and SnowflakeDataSink::write_all * Update datafusion-table-providers to 8df061143d7f5ce7640f531e74dddb471877642a * Fix Snowflake table path quoting using sqlparser-based identifier parsing * cleanup logging * Fix OR predicate precedence in Snowflake filters_to_sql * Fix clippy: rename new to create, pass read_provider by ref * Fix coercion of table_provider to dyn TableProvider at create call site * Fix clippy: use unwrap_err instead of assert!(is_err()) in tests * Fix clippy: use expect_err instead of unwrap_err in tests * fix formatting * fix(snowflake): fix clippy lint errors in provider and write tests * fix(snowflake): use expect_err instead of unwrap_err in tests * fix formatting --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: ewgenius <hey@ewgenius.me> Co-authored-by: Evgenii Khramkov <evgenii@spice.ai> * fix: Update tpch benchmark snapshots for federated/mssql[catalog].yaml (#10763) * fix: Update tpch benchmark snapshots for federated/mssql[catalog].yaml * Trigger CI * Trigger CI --------- Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> * fix: Update tpch benchmark snapshots for federated/mssql.yaml (#10762) * fix: Update tpch benchmark snapshots for federated/mssql.yaml * Trigger CI * Trigger CI --------- Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> * Improve Turso benchmark robustness: fix configs, query overrides, and timeouts (#10765) * fix(benchmarks): redact `file_groups` in explain plan snapshots to fix non-deterministic test failures (SF100) (#10769) * Fixes for views and resolved tables on 'spice refresh' CLI (#10759) * feat: Allow full version tags in spicepod version (#10748) * feat: Allow full version tags in spicepod version * review: address comments * chore: expect expect * review: Address comment * chore: fmt * review: Address comments * Update xAI default model and handle Grok model retirements (#10723) * Update xAI model references for Grok model retirement (May 15, 2026) - Change default xAI model from retired `grok-3` to `grok-4.3` - Replace retired `grok-4-1-fast-non-reasoning` with `grok-4.20-non-reasoning` in tests and configs Closes #10721 * fix: apply cargo fmt and rename grok41 to grok420 in dispatch configs Apply cargo fmt to match repo style on the xAI test creator closure, and update bird-bench testoperator dispatch configs to reference the renamed grok420 model alias from test/spicepods/models/text-to-sql.yaml (was missed when renaming grok41 -> grok420 in this PR). --------- Co-authored-by: claudespice <claude@spice.ai> * Update openapi.json (#10741) Co-authored-by: Spice Schema Bot <schema-bot@spice.ai> Co-authored-by: claudespice <claude@spice.ai> * Rate limit metrics HTTP endpoint (#10162) * Add auth and rate limiting to cluster APIs and metrics server - Add gRPC auth interceptor to ClusterServiceServer using EndpointAuth - Add separate cluster_metrics_limit (100/min) to RateLimits - Rate-limit GetMetrics and GetTaskHistory gRPC endpoints - Rate-limit Prometheus metrics HTTP server (/metrics, not /health) - Thread EndpointAuth through cluster initialization * Fix PR review: correct retry-after calculation, add Retry-After headers - Fix wait_time_from using earliest_possible (always 0s) to use DefaultClock::default().now() for accurate retry duration - Add Retry-After HTTP header to 429 response in metrics server - Add retry-after gRPC metadata to RESOURCE_EXHAUSTED status in cluster service, matching existing flight rate limiter pattern * refactor(cluster): remove unused endpoint_auth parameter from cluster initialization * test: cover task history rate limiting * refactor: remove rate limiter from cluster service and update related comments Co-authored-by: Copilot <copilot@github.com> * fix: add missing RateLimiter import in servers.rs * fix: only rate-limit metrics HTTP endpoint * test: add integration tests for ClusterService client and server * fix: satisfy cluster service test lint * fix: address PR review on metrics endpoint rate limiting Restrict the metrics HTTP server to /health and /metrics only — other paths now return 404 instead of serving Prometheus output, which closes a bypass where /, /metrics/, or any other path could be scraped without consuming the rate-limit bucket. Rename `cluster_metrics_limit` to `metrics_endpoint_limit` to reflect that it throttles every /metrics request (local scrapes and the ?scope=cluster fan-out alike), and update the docstring to call out the tradeoff explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: cover /health bypass and metrics-endpoint rate limit end-to-end Update the endpoint-auth integration test to probe /metrics instead of /, since the metrics server now returns 404 for unknown paths. Add a unit test that asserts /health continues to respond after the /metrics bucket is exhausted, so a regression that puts health probes on the same limiter would fail this suite instead of silently breaking readiness checks. Add an end-to-end test that boots Runtime::start_servers with a burst-1 metrics_endpoint_limit and exercises the live HTTP listener: first /metrics returns 200, second returns 429 with Retry-After, /health still returns 200. Factored start_spice_test_app into a sibling that returns the metrics port to avoid touching the 26 existing callers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(metrics): remove unused hyper imports Fixes -D warnings build failure flagged by Copilot review on PR #10162. hyper::body (the module re-export) and hyper::server::conn::http1::Builder were unused; only Incoming and AutoBuilder are referenced. --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Phillip LeBlanc <879445+phillipleblanc@users.noreply.github.com> Co-authored-by: claudespice <claude@spice.ai> * Fix 'wait_until_dependent_tables_are_ready' for catalogs (#10758) * Fix 'wait_until_dependent_tables_are_ready' for catalogs * fix: use if let instead of match in wait_until_dependent_tables_are_ready * Add local-spiced and cayenne-flightsql subcommands to spidapter (#10618) * split out local spiced and flightSQL cayenne mode * Remove MERGE support from cayenne-flightsql `MERGE INTO` via Flight SQL requires `plan_local_merge` + `DmlExtensionPlanner` and the `SqlStatementExecutor` hook in datafusion-flightsql — changes that belong in a separate PR. Remove `CayenneMergeExecutor`, `DmlExtensionPlanner`, and the `with_statement_executor` wiring. Keep `DdlExtensionPlanner` + `DdlAnalyzerRule` which are needed for `CREATE TABLE` to produce real Cayenne (Vortex) tables rather than DataFusion in-memory ones. * fix(datafusion-flightsql): coerce ingest batch types and expand view types in query results Two type-mismatch fixes discovered during spicebench cayenne-flightsql runs: 1. do_put_raw: incoming ADBC batches may have Timestamp(µs) while the table schema says Timestamp(ns). Add coerce_batches_to_schema to cast columns to match the table schema before insert_into. 2. sql_to_flight_stream: Cayenne/Vortex returns Utf8View columns but GetFlightInfo advertises LargeUtf8 (via expand_views_schema). Cast each result batch with cast_view_columns so the data matches the advertised schema. * fix: wrap SQLite and DataFusion in backticks for clippy doc_markdown * cleanup * clean * clippy * fix: box spidapter Commands variants to satisfy large_enum_variant The Stdio variant was 536 bytes versus 200 bytes for CayenneFlightsql, which tripped clippy::large_enum_variant on stricter Rust 1.94 lint rules. Box all three variants for consistent pointer-sized members; clap Subcommand handles Box-wrapped arg structs natively, and the existing match arms compile unchanged via Box's Deref coercion. --------- Co-authored-by: claudespice <claude@spice.ai> * fix: enable streaming append for Kafka with Cayenne accelerator (#10269) (#10780) Allow `refresh_mode: append` to use a connector-provided `append_stream` when the accelerator engine is Cayenne. Previously, the Cayenne engine was unconditionally excluded from the append-stream attachment path, so a Kafka dataset with `engine: cayenne` and `refresh_mode: append` failed because no streaming source was registered and Cayenne could not fall through to its primary_key validation (since Kafka messages have neither time_column nor primary_key). The check now attempts to attach the source's `append_stream` first. If the source provides one (Kafka, SpiceAI), it is used for all engines including Cayenne. If it does not, Cayenne still falls back to its primary_key validation; other engines require time_column. Co-authored-by: Claude <noreply@anthropic.com> * build(deps): bump the github-actions-dependencies group with 2 updates (#10767) Bumps the github-actions-dependencies group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/checkout` from 6.0.1 to 6.0.2 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6.0.1...de0fac2e4500dabe0009e67214ff5f5447ce83dd) Updates `github/codeql-action` from 4.35.3 to 4.35.4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/e46ed2cbd01164d986452f91f178727624ae40d7...68bde559dea0fdcac2102bfdf6230c5f70eb485e) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-dependencies - dependency-name: github/codeql-action dependency-version: 4.35.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jack Eadie <jack@spice.ai> * fix: Update benchmark snapshots May-11 (#10774) * fix(benchmarks): redact `file_groups` in explain plan snapshots to fix non-deterministic test failures (SF100) * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-turso[file].yaml * fix: Update tpch benchmark snapshots for accelerated/postgres-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/mysql-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-cayenne[file]-partitioned.yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-cayenne[file]-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-arrow-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/dynamodb-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-cayenne[file].yaml * fix: Update tpch benchmark snapshots for accelerated/mongodb-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-duckdb[file]-partitioned.yaml * fix: Update tpch benchmark snapshots for accelerated/file[parquet]-cayenne[file]turso.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/dynamodb-duckdb[file].yaml * fix: Update tpch benchmark snapshots for accelerated/indexes/file[parquet]-turso[file]-indexes.yaml * fix: Update tpch benchmark snapshots for accelerated/spicecloud-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[file]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-cayenne[file]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[memory]-on_zero_results.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow-partitioned.yaml * fix: Update tpch benchmark snapshots for federated/file[parquet].yaml * fix: Update tpch benchmark snapshots for federated/glue[catalog].yaml * fix: Update tpch benchmark snapshots for federated/iceberg[catalog].yaml * fix: Update tpch benchmark snapshots for federated/spicecloud[catalog].yaml * fix: Update tpch benchmark snapshots for federated/oracle[catalog].yaml * fix: Update tpch benchmark snapshots for federated/databricks[delta_lake].yaml * fix: Update tpch benchmark snapshots for federated/oracle.yaml * fix: Update tpch benchmark snapshots for federated/databricks[catalog].yaml * fix: Update tpch benchmark snapshots for federated/mongodb.yaml * fix: Update tpch benchmark snapshots for federated/abfs_standard_versioned[parquet].yaml * fix: Update tpch benchmark snapshots for federated/glue[csv].yaml * fix: Update tpch[parameterized] benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpch benchmark snapshots for federated/mssql.yaml * fix: Update tpch benchmark snapshots for federated/spicecloud.yaml * fix: Update tpch benchmark snapshots for federated/iceberg[hadoop].yaml * fix: Update tpch benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpch benchmark snapshots for federated/abfs[parquet].yaml * fix: Update tpch benchmark snapshots for federated/glue[parquet].yaml * fix: Update tpch benchmark snapshots for federated/mssql[catalog].yaml * fix: Update tpch benchmark snapshots for federated/dynamodb.yaml * fix: Update tpch[parameterized] benchmark snapshots for federated/file[parquet].yaml * fix: Update tpcds benchmark snapshots for accelerated/postgres-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/file[parquet]-cayenne[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * fix: Update tpcds benchmark snapshots for accelerated/databricks[delta_lake]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/spicecloud-arrow.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[file]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-cayenne[file]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/on_zero_results/file[parquet]-duckdb[memory]-on_zero_results.yaml * fix: Update tpcds benchmark snapshots for accelerated/s3[parquet]-arrow-partitioned.yaml * fix: Update tpcds benchmark snapshots for federated/file[parquet].yaml * fix: Update tpcds benchmark snapshots for federated/databricks[delta_lake].yaml * fix: Update tpcds benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpcds benchmark snapshots for federated/abfs[parquet].yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-turso[file].yaml * fix: Update clickbench benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * fix: Update clickbench benchmark snapshots for federated/s3[parquet].yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-arrow.yaml * fix: Update tpch benchmark snapshots for accelerated/s3[parquet]-cayenne[file].yaml * Trigger CI * Trigger CI * Trigger CI * Trigger CI --------- Co-authored-by: Sergei Grebnov <sergei.grebnov@gmail.com> Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> * Implement FlightSQL CommandStatementSubstraitPlan support (#10761) * Add FlightSQL CommandStatementSubstraitPlan support Decode the SubstraitPlan wrapped in the FlightSQL command, lower it to a DataFusion LogicalPlan via datafusion-substrait, and run it through the same execution path as a SQL statement so get_flight_info and do_get return the resolved schema and a streaming result. Read-only auth is enforced against the lowered plan, and a stable cache key derived from the plan bytes lets identical Substrait plans share results-cache entries. Update GetSqlInfo to advertise Substrait support. https://claude.ai/code/session_01WoHcN52LcrFb9rzWxRzUBk * Address review: copyright year and sha256_hex allocation - Bump copyright header to 2024-2026 on the new file. - Replace per-byte `format!` allocation in sha256_hex with `write!` into a preallocated String. https://claude.ai/code/session_01WoHcN52LcrFb9rzWxRzUBk * Address review: add tests for CommandStatementSubstraitPlan handlers - Split `decode_plan` so the protobuf-decode validation rules (missing plan, empty bytes, undecodable bytes) can be unit-tested without spinning up a DataFusion session. - Add unit tests covering missing/empty/invalid plan bytes, cache-key namespacing, and the sha256_hex helper. - Add integration tests (`tests/flight/statement_substrait_plan.rs`) that build a Substrait plan via `datafusion-substrait`'s producer and exercise the full FlightSQL round-trip: GetFlightInfo + DoGet for a valid plan, plus invalid_argument rejection for missing plan and un-decodable bytes, and result stability across repeated execution. https://claude.ai/code/session_01WoHcN52LcrFb9rzWxRzUBk * Replace insta snapshot with direct string assertion Copilot flagged that `insta::assert_snapshot!("substrait_round_trip", ...)` would fail in CI without a checked-in `.snap` file. Since the expected output for `SELECT 1 AS x, 'spice' AS name` is fully self-contained, drop the snapshot and assert against a literal string instead — no separate snapshot file to maintain. https://claude.ai/code/session_01WoHcN52LcrFb9rzWxRzUBk * Harden Substrait integration tests against round-trip variation Integration Tests (part 3) failed on the last run; with no log access I can't pinpoint which assertion fired, but the literal pretty-printed string match in `test_substrait_plan_round_trip` is the most brittle piece and would silently break if Substrait round-tripping reshapes column aliases or surfaces an Int32 instead of Int64. Replace it with shape + value assertions (1 row, 2 columns, output contains "spice" and a standalone "1"). Also swap the "invalid bytes" pattern in both unit and integration tests from `[0xff; 4]` to `[0x0a, 0x10]` — a length-delimited tag claiming 16 bytes of payload with none supplied is unambiguously a prost buffer-underflow. https://claude.ai/code/session_01WoHcN52LcrFb9rzWxRzUBk * test(flightsql): use valid Substrait bytes for cache key test --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Phillip LeBlanc <879445+phillipleblanc@users.noreply.github.com> * feat(ducklake): Support INSERT on catalog tables with read_write access (#10744) * fix(ducklake): Support INSERT on catalog tables with read_write access * DuckDbFederatedTableWriter * Update * Fix lint * Improve * Update SHA * fix(ducklake): Preserve federation pushdown for read_write tables * perf(cayenne): lock-free deletion caches with bloom-prefiltered probe (#10756) * docs(cayenne): add performance redesign plan Three structural bets (lock-free deletion caches with PK Selection pushdown, per-file zone maps in the metastore, merge-on-read upsert with compaction) with sequencing, validation plan, and open decisions. No code changes. * perf(cayenne): replace deletion-cache RwLocks with ArcSwap, add bloom-prefiltered probe Bet A from the cayenne perf redesign, in one PR. PkDeletionStrategyWithCache previously held Arc<RwLock<Arc<HashMap<...>>>> for every deletion and insert cache. Each scan acquired a read guard, cloned the inner Arc, then dropped the guard; refresh took a write guard and swapped it. The outer RwLock therefore serialised readers against any writer. Filter execs probed those raw HashMaps once per row inside the deletion-filter stream, with no bloom prefilter. This change: 1. Freezes DeletionIndex / KeyDeletionIndex (provider/deletion_index.rs) to immutable post-construction types: a plain HashMap plus a BloomFilter, no internal RwLock. Construct via from_map; rebuild via extend_max; readers only call get / might_contain / len / is_empty. 2. Replaces the cache fields in PkDeletionStrategyWithCache with Arc<ArcSwap<DeletionIndex>>, Arc<ArcSwap<KeyDeletionIndex>>, and Arc<ArcSwap<HashMap<String, RoaringBitmap>>> for position-based. Readers use load_full() (wait-free); writers build a new snapshot off the hot path and store() it atomically. refresh_from is a pair of stores. 3. Routes Int64PkDeletionFilterExec and KeyBasedDeletionFilterExec through the bloom-prefiltered DeletionIndex / KeyDeletionIndex directly. The hot loop in each filter stream now iterates the PK column slice (or RowConverter-converted rows) once, builds a keep mask via bloom-prefiltered probes, and applies it via arrow::compute::filter_record_batch in a single shot. 4. Threads the same bloom-prefiltered indexes into the upsert keyset builder (process_batches_into_keyset) so dedup probes during prepare_stream_for_insert also benefit from the bloom prefilter. 5. Updates every callsite (table.rs, delete/sink.rs, delete/sink/position_based.rs, vortex_format.rs) to the new shape. No public catalog API change; no on-disk format change; no metastore migration. Validation: - New micro-bench file crates/cayenne/benches/deletion_index_probe.rs: - deletion_index_int64_probe at deletion ratios {0, 0.001, 0.01, 0.1, 0.5} on an 8192-row Int64 PK column. - deletion_index_row_keys_probe at the same ratios for composite keys. - deletion_index_concurrent_load_under_publish: a writer publishes 100k-entry snapshots in a loop while criterion measures load_full() throughput. Demonstrates the wait-free read path. - cargo check -p cayenne: clean. - cargo build --benches -p cayenne: clean. - Full cargo test -p cayenne hit linker OOM in this sandbox while linking the large per-test binaries; CI will run the full suite. Adds arc-swap to workspace dependencies. Bet B (per-file zone maps in the metastore) and Bet C (merge-on-read upsert + compaction) remain as follow-up PRs. * refactor: simplify error handling and improve performance in deletion-related methods * refactor: optimize key index building in deletion benchmarks --------- Co-authored-by: Claude <noreply@anthropic.com> * Fix cayenne append mode constraint test and improve timeout handling (#10785) * fix(tests): re-enable test_cayenne_append_mode_requires_constraint The test was disabled because it lacked `register_test_connectors()`, so the Postgres connector was never registered. The test also used `tokio::spawn` + a fixed 2s sleep, which was flaky. Register the test connectors before building the runtime, and replace the spawn/sleep with the same `tokio::select!` timeout pattern used by `test_hash_index_requires_primary_key`. `load_components()` retries failed dataset initialization indefinitely, so the select bounds the wait while letting initialization fail and confirm the dataset never became queryable. Fixes #7860 * test(cayenne): poll dataset status and shut down runtime cleanly Address review feedback on the bounded-wait approach. The previous `tokio::select!` could drop the `load_components()` future before initialization tasks were even spawned, risking a false positive on the "table not found" assertion, and the spawned retry loop would otherwise outlive the test. Spawn `load_components()` so the first attempt actually runs, poll the dataset's `ComponentStatus` for the expected `Error` state (validation fired), then call `rt.shutdown()` to release the infinite-retry loop and abort the spawned task before the docker container is dropped. --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: use checked arithmetic for Turso integer-millis timestamp read path (#10786) The Turso connector's read path converts stored integer milliseconds to the target timestamp unit using plain `*` multiplication. For Nanosecond and Microsecond units, this silently wraps on overflow in release builds for dates outside ~1677-2262, producing corrupt timestamp values. The write path already uses `checked_mul` (line 1902-1917), but the read path at line 691-692 was missed. The RFC3339 text read path also handles this correctly via `timestamp_nanos_opt()`. Replace unchecked `*` with `checked_mul`, returning NULL on overflow (consistent with how `timestamp_nanos_opt()` returns `None`). * fix(ScyllaDB): disable physical filter pushdown (#10772) * fix(ScyllaDB): disable physical filter pushdown * Add ScyllaDB override * fix: Update tpch benchmark snapshots for federated/scylladb.yaml * Update sha --------- Co-authored-by: Spice Benchmark Snapshot Update Bot <spiceaibot@spice.ai> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * build(deps): bump the aws-sdk group across 1 directory with 12 updates (#10768) Bumps the aws-sdk group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [aws-config](https://github.com/smithy-lang/smithy-rs) | `1.8.15` | `1.8.16` | | [aws-sdk-bedrockruntime](https://github.com/awslabs/aws-sdk-rust) | `1.129.0` | `1.130.0` | | [aws-sdk-cognitoidentity](https://github.com/awslabs/aws-sdk-rust) | `1.98.0` | `1.99.0` | | [aws-sdk-cognitoidentityprovider](https://github.com/awslabs/aws-sdk-rust) | `1.114.0` | `1.116.0` | | [aws-sdk-dynamodb](https://github.com/awslabs/aws-sdk-rust) | `1.110.0` | `1.111.0` | | [aws-sdk-dynamodbstreams](https://github.com/awslabs/aws-sdk-rust) | `1.98.0` | `1.99.0` | | [aws-sdk-glue](https://github.com/awslabs/aws-sdk-rust) | `1.142.1` | `1.145.0` | | [aws-sdk-s3](https://github.com/awslabs/aws-sdk-rust) | `1.129.0` | `1.132.0` | | [aws-sdk-s3vectors](https://github.com/awslabs/aws-sdk-rust) | `1.23.0` | `1.24.0` | | [aws-sdk-secretsmanager](https://github.com/awslabs/aws-sdk-rust) | `1.103.0` | `1.104.0` | Updates `aws-config` from 1.8.15 to 1.8.16 - [Release notes](https://github.com/smithy-lang/smithy-rs/releases) - [Changelog](https://github.com/smithy-lang/smithy-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-rs/commits) Updates `aws-runtime` from 1.7.2 to 1.7.3 - [Release notes](https://github.com/smithy-lang/smithy-rs/releases) - [Changelog](https://github.com/smithy-lang/smithy-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-rs/commits) Updates `aws-sdk-bedrockruntime` from 1.129.0 to 1.130.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-cognitoidentity` from 1.98.0 to 1.99.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-cognitoidentityprovider` from 1.114.0 to 1.116.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-dynamodb` from 1.110.0 to 1.111.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-dynamodbstreams` from 1.98.0 to 1.99.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-glue` from 1.142.1 to 1.145.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-s3` from 1.129.0 to 1.132.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-s3vectors` from 1.23.0 to 1.24.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-secretsmanager` from 1.103.0 to 1.104.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) Updates `aws-sdk-sts` from 1.102.0 to 1.103.0 - [Release notes](https://github.com/awslabs/aws-sdk-rust/releases) - [Commits](https://github.com/awslabs/aws-sdk-rust/commits) --- updated-dependencies: - dependency-name: aws-config dependency-version: 1.8.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws-sdk - dependency-name: aws-runtime dependency-version: 1.7.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws-sdk - dependency-name: aws-sdk-bedrockruntime dependency-version: 1.130.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-cognitoidentity dependency-version: 1.99.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-cognitoidentityprovider dependency-version: 1.116.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-dynamodb dependency-version: 1.111.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-dynamodbstreams dependency-version: 1.99.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-glue dependency-version: 1.145.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-s3 dependency-version: 1.132.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-s3vectors dependency-version: 1.24.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-secretsmanager dependency-version: 1.104.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk - dependency-name: aws-sdk-sts dependency-version: 1.103.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws-sdk ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Luke Kim <80174+lukekim@users.noreply.github.com> Co-authored-by: Jack Eadie <jack@spice.ai> * fix: ignore field metadata in schema compatibility check in index_table_scan (#10778) * fix: ignore field metadata in schema compatibility check in index_table_scan Arrow's Field PartialEq includes field-level metadata, so two schemas that are structurally identical (same name, type, nullability) can compare as unequal when FTS or another index attaches metadata to fields in the advertised schema but not in the incoming batch. This caused a spurious 'Input stream produced batch with unexpected schema' error when FTS was enabled on an accelerated dataset — the error message showed identical Expected and Got schemas because schema_signature intentionally omits metadata, but the fields() != fields() check was still failing on the hidden metadata difference. Fix: replace both fields() != fields() comparisons with a new fields_structurally_equal() helper that compares only name, data type, and nullability, ignoring metadata. Fixes #10223 * address PR review: use arrow_tools::verify_schema, add field-level metadata regression test - Replace bespoke fields_structurally_equal() helper with the existing arrow_tools::verify_schema(), which already does a metadata-ignoring structural comparison (name + data type, with Utf8-family and null placeholder tolerances). Avoids duplicating logic. - Add arrow_tools as a direct dependency of runtime-datafusion-index. - Fix misplaced doc comment above schema_signature() (previously described the now-deleted helper; now correctly documents the diagnostic formatting function). - Add pipeline_tolerates_field_level_metadata_difference regression test that exercises field-level (not schema-level) metadata differences, which is the exact scenario from #10223 (FTS attaching metadata to indexed fields). * update Cargo.lock * fix import * fix: use arrow_tools::schema::verify_schema at all call sites * fix: add backticks around `PartialEq` in doc comment to satisfy clippy::doc_markdown * Upgrade to DuckDB v1.5.2 (#10788) * feat(tls): public mTLS for HTTP and Flight (channel + identity modes) (#10753) Adds per-protocol mTLS to the public HTTP and Flight listeners on top of the existing on-disk TLS hot-reload foundation. Default behavior is unchanged: `client_auth: none` is the default and produces a `with_no_client_auth` rustls server config. ## Surface - New `runtime.tls.client_auth` (`none` | `required`) plus `client_ca_file` / inline `client_ca` in spicepod and matching `--tls-client-auth`, `--tls-client-ca-file`, `--tls-client-ca` CLI flags. Validation: `required` needs a CA; `none` must NOT have one. - `ReloadableServerCerts` extended to optionally own a swappable `WebPkiClientVerifier` via a second `ArcSwap`. CA, cert, and key rotate atomically on a single dispatcher tick. Exported through a thin `ReloadableClientVerifier` shim so `build_server_config` can call `with_client_cert_verifier` against an `Arc<dyn ClientCertVerifier>` while the inner verifier hot-swaps off the handshake hot path. - New `runtime_auth::mtls` module: `IdentitySource` enum (`Anonymous` | `Channel` | `RuntimeAuth`), `MtlsPrincipal`, `ChannelIdentity`, `principal_from_cert`, `channel_identity_from_cert`. `stable_id` namespaces: `spiffe:` > `mtls:dn:` > `mtls:fp:` so rotated certs of the same identity keep their cache namespace. - HTTP middleware (`runtime::http::mtls`) snapshots the verified peer chain off the rustls connection into a per-connection `Extension<Arc<PerConnTls>>`, then a per-request layer reads it and \u2014 in `Channel` mode \u2014 sets the principal on the request's `AuthRequestContext`. Always sets `ChannelIdentity` for audit. - Flight middleware (`runtime::flight::mtls`) does the same against tonic's `TlsConnectInfo` (gated by the `tls-connect-info` feature added to the workspace tonic dep). - Both `runtime_auth::layer::{http,flight}` short-circuit when `AuthRequestContext::auth_principal()` is already set, so a verified cert in identity mode replaces a bearer token without rejecting the request. - `EndpointAuth::with_identity_source` and the spiced entrypoint resolve the effective `IdentitySource` from `(runtime.auth, client_auth)` and thread it into both protocol stacks. ## Tests - 9 `runtime_auth::mtls` unit tests covering the principal resolution / stable-id paths. - 4 `spicepod` parser tests for `client_auth` modes (default, required + file CA, required + inline CA, unknown-mode rejection). - 5 `spiced` validation tests covering the `required` / `none` cross product against CA presence. - 2 end-to-end integration tests in `tests/mtls_public/` exercising both `Channel` and `Anonymous` modes against real `Runtime::start_servers` listeners with rcgen-generated PKI: no-cert and foreign-CA peers are rejected at the rustls handshake; a CA-signed peer reaches `/health` and succeeds at Flight `get_flight_info`. ## Out of scope (deferred follow-ups) - SPIFFE URI SAN extraction, native Workload API client - Per-cert authorization rules / trust-domain pinning - The mTLS-as-channel API-key end-to-end test (covered indirectly by existing API-key auth tests + per-protocol mTLS unit tests) * Add Arrow primary key upserts (#10749) * Add Arrow primary key upserts * feat(debezium): add support for full-row deletes and updates without primary keys * style(write): format code for better readability in row_key_counts function * feat(refresh): add functions for selecting and deleting rows in Arrow provider * fix(refresh): improve error handling for row index conversion and streamline row selection logic * refactor: streamline hash_index checks and improve logging for Arrow engine acceleration * fix(debezium): add error handling for invalid change event schema * fix: update descriptions and warnings for hash index and Debezium connector * R…
1 parent 8ce2b7f commit efc8d00

232 files changed

Lines changed: 1926 additions & 1309 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/runtime/tests/models/search.rs

Lines changed: 784 additions & 170 deletions
Large diffs are not rendered by default.

crates/runtime/tests/models/snapshots/integration_models__models__search__additional_columns_metadata_response.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: normalize_search_response(resp, round_scores)
3+
expression: normalize_search_response(resp)
44
snapshot_kind: text
55
---
66
{
@@ -20,7 +20,7 @@ snapshot_kind: text
2020
"primary_key": {
2121
"id": 863
2222
},
23-
"_score": "0.94"
23+
"_score": "[score]"
2424
},
2525
{
2626
"data": {
@@ -36,7 +36,7 @@ snapshot_kind: text
3636
"primary_key": {
3737
"id": 1316
3838
},
39-
"_score": "0.92"
39+
"_score": "[score]"
4040
},
4141
{
4242
"data": {
@@ -52,7 +52,7 @@ snapshot_kind: text
5252
"primary_key": {
5353
"id": 590
5454
},
55-
"_score": "0.91"
55+
"_score": "[score]"
5656
},
5757
{
5858
"data": {
@@ -68,7 +68,7 @@ snapshot_kind: text
6868
"primary_key": {
6969
"id": 6
7070
},
71-
"_score": "0.91"
71+
"_score": "[score]"
7272
}
7373
]
7474
}

crates/runtime/tests/models/snapshots/integration_models__models__search__additional_columns_response.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: normalize_search_response(resp, round_scores)
3+
expression: normalize_search_response(resp)
44
snapshot_kind: text
55
---
66
{
@@ -19,7 +19,7 @@ snapshot_kind: text
1919
"primary_key": {
2020
"id": 863
2121
},
22-
"_score": "0.89"
22+
"_score": "[score]"
2323
},
2424
{
2525
"data": {
@@ -34,7 +34,7 @@ snapshot_kind: text
3434
"primary_key": {
3535
"id": 1316
3636
},
37-
"_score": "0.84"
37+
"_score": "[score]"
3838
},
3939
{
4040
"data": {
@@ -49,7 +49,7 @@ snapshot_kind: text
4949
"primary_key": {
5050
"id": 590
5151
},
52-
"_score": "0.83"
52+
"_score": "[score]"
5353
},
5454
{
5555
"data": {
@@ -64,7 +64,7 @@ snapshot_kind: text
6464
"primary_key": {
6565
"id": 6
6666
},
67-
"_score": "0.83"
67+
"_score": "[score]"
6868
}
6969
]
7070
}

crates/runtime/tests/models/snapshots/integration_models__models__search__basic_additional_columns_response.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: normalize_search_response(resp, round_scores)
3+
expression: normalize_search_response(resp)
44
snapshot_kind: text
55
---
66
{
@@ -19,7 +19,7 @@ snapshot_kind: text
1919
"primary_key": {
2020
"id": 863
2121
},
22-
"_score": "0.89"
22+
"_score": "[score]"
2323
},
2424
{
2525
"data": {
@@ -34,7 +34,7 @@ snapshot_kind: text
3434
"primary_key": {
3535
"id": 1316
3636
},
37-
"_score": "0.84"
37+
"_score": "[score]"
3838
},
3939
{
4040
"data": {
@@ -49,7 +49,7 @@ snapshot_kind: text
4949
"primary_key": {
5050
"id": 590
5151
},
52-
"_score": "0.83"
52+
"_score": "[score]"
5353
},
5454
{
5555
"data": {
@@ -64,7 +64,7 @@ snapshot_kind: text
6464
"primary_key": {
6565
"id": 6
6666
},
67-
"_score": "0.83"
67+
"_score": "[score]"
6868
}
6969
]
7070
}

crates/runtime/tests/models/snapshots/integration_models__models__search__basic_basic_response.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: normalize_search_response(resp, round_scores)
3+
expression: normalize_search_response(resp)
44
snapshot_kind: text
55
---
66
{
@@ -16,7 +16,7 @@ snapshot_kind: text
1616
"primary_key": {
1717
"id": 863
1818
},
19-
"_score": "0.89"
19+
"_score": "[score]"
2020
},
2121
{
2222
"dataset": "qs",
@@ -28,7 +28,7 @@ snapshot_kind: text
2828
"primary_key": {
2929
"id": 1316
3030
},
31-
"_score": "0.84"
31+
"_score": "[score]"
3232
},
3333
{
3434
"dataset": "qs",
@@ -40,7 +40,7 @@ snapshot_kind: text
4040
"primary_key": {
4141
"id": 590
4242
},
43-
"_score": "0.83"
43+
"_score": "[score]"
4444
},
4545
{
4646
"dataset": "qs",
@@ -52,7 +52,7 @@ snapshot_kind: text
5252
"primary_key": {
5353
"id": 6
5454
},
55-
"_score": "0.83"
55+
"_score": "[score]"
5656
}
5757
]
5858
}

crates/runtime/tests/models/snapshots/integration_models__models__search__basic_vector_search_sql_filters.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@ snapshot_kind: text
77
{
88
"id": 863,
99
"answer": "Choices create costs and benefits.",
10-
"_score": "0.87"
10+
"_score": "[score]"
1111
},
1212
{
1313
"id": 6,
1414
"answer": "<refined solution> \n\n*(Repeated for each question-answer pair above.)",
15-
"_score": "0.82"
15+
"_score": "[score]"
1616
},
1717
{
1818
"id": 438,
1919
"answer": "The Cat does not need to drink, as it obtains sufficient water from its prey.",
20-
"_score": "0.80"
20+
"_score": "[score]"
2121
},
2222
{
2323
"id": 1041,
2424
"answer": "A common thermometer operates on the principle that matter usually expands when it absorbs heat energy.",
25-
"_score": "0.77"
25+
"_score": "[score]"
2626
}
2727
]

crates/runtime/tests/models/snapshots/integration_models__models__search__basic_vector_search_sql_projection.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,27 @@ snapshot_kind: text
99
"answer": "Choices create costs and benefits.",
1010
"question": "What is created by making a choice?",
1111
"subject": "economics",
12-
"_score": "0.89"
12+
"_score": "[score]"
1313
},
1414
{
1515
"id": 1316,
1616
"answer": "\\(\\boxed{2a + 4b}\\)",
1717
"question": "Write down in simplest form \\( 5a - 2b - 3a + 6b \\).",
1818
"subject": "math",
19-
"_score": "0.84"
19+
"_score": "[score]"
2020
},
2121
{
2222
"id": 590,
2323
"answer": "\\(\\boxed{x = A + E}\\)",
2424
"question": "Make \\( x \\) the subject of the formula \\( x - A = E \\).",
2525
"subject": "math",
26-
"_score": "0.83"
26+
"_score": "[score]"
2727
},
2828
{
2929
"id": 6,
3030
"answer": "<refined solution> \n\n*(Repeated for each question-answer pair above.)",
3131
"question": "<refined question>",
3232
"subject": "medicine",
33-
"_score": "0.83"
33+
"_score": "[score]"
3434
}
3535
]

crates/runtime/tests/models/snapshots/integration_models__models__search__basic_with_where_response.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: normalize_search_response(resp, round_scores)
3+
expression: normalize_search_response(resp)
44
snapshot_kind: text
55
---
66
{
@@ -16,7 +16,7 @@ snapshot_kind: text
1616
"primary_key": {
1717
"id": 863
1818
},
19-
"_score": "0.87"
19+
"_score": "[score]"
2020
},
2121
{
2222
"dataset": "qs",
@@ -28,7 +28,7 @@ snapshot_kind: text
2828
"primary_key": {
2929
"id": 6
3030
},
31-
"_score": "0.82"
31+
"_score": "[score]"
3232
}
3333
]
3434
}

crates/runtime/tests/models/snapshots/integration_models__models__search__hf_all_datasets_response.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: "normalize_search_response(resp, round_scores)"
3+
expression: normalize_search_response(resp)
44
---
55
{
66
"duration_ms": "duration_ms_val",
77
"results": [
88
{
9-
"_score": "0.94",
9+
"_score": "[score]",
1010
"dataset": "spice.public.catalog_page_with_chunking",
1111
"matches": {
1212
"cp_description": [
@@ -18,7 +18,7 @@ expression: "normalize_search_response(resp, round_scores)"
1818
}
1919
},
2020
{
21-
"_score": "0.94",
21+
"_score": "[score]",
2222
"dataset": "spice.public.catalog_page_with_chunking_no_pk",
2323
"matches": {
2424
"cp_description": [

crates/runtime/tests/models/snapshots/integration_models__models__search__hf_basic_response.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
22
source: crates/runtime/tests/models/search.rs
3-
expression: "normalize_search_response(resp, round_scores)"
3+
expression: normalize_search_response(resp)
44
---
55
{
66
"duration_ms": "duration_ms_val",
77
"results": [
88
{
9-
"_score": "0.89",
9+
"_score": "[score]",
1010
"data": {
1111
"i_color": "yellow",
1212
"i_item_id": "AAAAAAAAKAAAAAAA"
@@ -22,7 +22,7 @@ expression: "normalize_search_response(resp, round_scores)"
2222
}
2323
},
2424
{
25-
"_score": "0.84",
25+
"_score": "[score]",
2626
"data": {
2727
"i_color": "wheat",
2828
"i_item_id": "AAAAAAAAIAAAAAAA"

0 commit comments

Comments
 (0)