supertable 2.2.4__tar.gz → 2.2.5__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.
- {supertable-2.2.4/supertable.egg-info → supertable-2.2.5}/PKG-INFO +1 -1
- {supertable-2.2.4 → supertable-2.2.5}/pyproject.toml +1 -1
- {supertable-2.2.4 → supertable-2.2.5}/setup.py +1 -1
- {supertable-2.2.4 → supertable-2.2.5}/supertable/__init__.py +1 -1
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/duckdb_lite.py +6 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/duckdb_pro.py +6 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/engine_common.py +58 -3
- supertable-2.2.5/supertable/engine/engine_config.py +153 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/executor.py +28 -18
- {supertable-2.2.4 → supertable-2.2.5}/supertable/redis_catalog.py +23 -8
- {supertable-2.2.4 → supertable-2.2.5}/supertable/redis_keys.py +4 -7
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_redis_key_prefix.py +1 -3
- {supertable-2.2.4 → supertable-2.2.5/supertable.egg-info}/PKG-INFO +1 -1
- {supertable-2.2.4 → supertable-2.2.5}/supertable.egg-info/SOURCES.txt +1 -0
- {supertable-2.2.4 → supertable-2.2.5}/LICENSE +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/README.md +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/requirements.txt +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/setup.cfg +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/admin.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/chain.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/consumers.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/crypto.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/events.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/export.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/logger.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/middleware.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/reader.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/retention.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/test_chain.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/test_crypto.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/test_emit.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/test_events.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/tests/test_retention.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/writer_parquet.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/audit/writer_redis.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/defaults.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/homedir.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/settings.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/tests/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/tests/test_defaults.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/tests/test_homedir.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/config/tests/test_settings.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/data_classes.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/data_reader.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/data_writer.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/__main__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/check_filter_builder.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/controller.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/data_writer_helpers.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/defaults.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/dummy_data.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/read_parquet_header.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s01_01_01_create_super_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s01_01_02_enable_mirroring_formats.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s01_02_create_roles.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s01_03_create_users.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_01_write_dummy_data.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_02_write_single_data.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_03_01_write_staging.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_03_02_create_pipe.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_04_01_write_monitoring_simple.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_04_02_write_monitoring_parallel.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s02_05_write_tombstone.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_01_read_data_error.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_02_01_read_super_data_ok.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_02_02_read_table_data_ok.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_03_read_meta.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_04_read_staging.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_06_01_read_roles.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_06_02_read_user.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_07_01_estimate_read.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_07_02_estimate_files.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s03_08_read_snapshot_history.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s04_01_03_delete_pipe.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s05_01_delete_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/quickstart/s05_02_delete_super_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/core.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/defaults.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/generate.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/load.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/demo/webshop/topup.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/data_estimator.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/engine_enum.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/plan_stats.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/spark_thrift.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/tests/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/tests/conftest.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/tests/test_dedup_read.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/engine/tests/test_engine.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/errors.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/benchmarks/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/benchmarks/benchmark_locking.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/benchmarks/measure_lock_speed.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/benchmarks/measure_lock_time.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/file_lock.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/redis_lock.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/tests/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/tests/test_file_lock.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/locking/tests/test_redis_lock.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/logging.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/meta_reader.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/mirroring/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/mirroring/mirror_delta.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/mirroring/mirror_formats.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/mirroring/mirror_iceberg.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/mirroring/mirror_parquet.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/monitoring/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/monitoring/partitions.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/monitoring_writer.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/plan_extender.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/processing.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/query_plan_manager.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/access_control.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/filter_builder.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/permissions.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/role_manager.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/row_column_security.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/tests/test_filter_builder.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/tests/test_rbac.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/tests/test_rbac_per_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/rbac/user_manager.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/redis_connector.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/redis_infra.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/simple_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/staging_area.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/azure_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/gcp_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/local_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/minio_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/s3_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/storage_factory.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/storage_interface.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/storage/tests/test_storage.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/super_pipe.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/super_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_align_to_schema_fix.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_create_if_missing.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_reader.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_reader_preflight.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_writer.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_writer_compact.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_writer_comprehensive.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_data_writer_tombstones.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_dedup_on_read_write.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_errors.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_meta_reader.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_monitoring_partitions.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_monitoring_sink_guard.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_newer_than.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_process_delete_only.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_processing.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_processing_compact_resources.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_query_sql.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_simple_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_small_file_compaction.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_super_table.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/tests/test_supertable_all.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/__init__.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/helper.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/profiler.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/sql_parser.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/tests/test_sql_parser_columns.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable/utils/timer.py +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable.egg-info/dependency_links.txt +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable.egg-info/entry_points.txt +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable.egg-info/requires.txt +0 -0
- {supertable-2.2.4 → supertable-2.2.5}/supertable.egg-info/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ long_description = readme.read_text(encoding="utf-8") if readme.exists() else ""
|
|
|
19
19
|
|
|
20
20
|
setup(
|
|
21
21
|
name="supertable",
|
|
22
|
-
version="2.2.
|
|
22
|
+
version="2.2.5",
|
|
23
23
|
description="SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.",
|
|
24
24
|
long_description=long_description,
|
|
25
25
|
long_description_content_type="text/markdown",
|
|
@@ -25,7 +25,7 @@ See the ``supertable.demo`` package for runnable end-to-end demos and the
|
|
|
25
25
|
project documentation for the full API surface.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
__version__ = "2.2.
|
|
28
|
+
__version__ = "2.2.5"
|
|
29
29
|
|
|
30
30
|
# Re-export the core public surface so users can do ``from supertable import …``
|
|
31
31
|
# instead of remembering submodule paths.
|
|
@@ -21,6 +21,7 @@ from supertable.engine.engine_common import (
|
|
|
21
21
|
create_reflection_view_with_presign_retry,
|
|
22
22
|
rewrite_query_with_hashed_tables,
|
|
23
23
|
init_connection,
|
|
24
|
+
apply_runtime_pragmas,
|
|
24
25
|
create_rbac_view,
|
|
25
26
|
create_dedup_view,
|
|
26
27
|
create_tombstone_view,
|
|
@@ -105,6 +106,7 @@ class DuckDBLite:
|
|
|
105
106
|
query_manager: QueryPlanManager,
|
|
106
107
|
timer_capture,
|
|
107
108
|
log_prefix: str = "",
|
|
109
|
+
engine_config=None,
|
|
108
110
|
) -> pd.DataFrame:
|
|
109
111
|
tried_presign = False
|
|
110
112
|
|
|
@@ -237,6 +239,10 @@ class DuckDBLite:
|
|
|
237
239
|
except Exception:
|
|
238
240
|
pass
|
|
239
241
|
|
|
242
|
+
# Re-apply live engine config (memory/threads/http/cache) so UI
|
|
243
|
+
# changes take effect on this persistent connection per query.
|
|
244
|
+
apply_runtime_pragmas(con, engine_config)
|
|
245
|
+
|
|
240
246
|
logger.debug(f"{log_prefix}[duckdb.lite] executing: {executing_query}")
|
|
241
247
|
result = con.execute(executing_query).fetchdf()
|
|
242
248
|
|
|
@@ -25,6 +25,7 @@ from supertable.engine.engine_common import (
|
|
|
25
25
|
make_presigned_list,
|
|
26
26
|
rewrite_query_with_hashed_tables,
|
|
27
27
|
init_connection,
|
|
28
|
+
apply_runtime_pragmas,
|
|
28
29
|
create_rbac_view,
|
|
29
30
|
create_dedup_view,
|
|
30
31
|
create_tombstone_view,
|
|
@@ -268,6 +269,7 @@ class DuckDBPro:
|
|
|
268
269
|
query_manager: QueryPlanManager,
|
|
269
270
|
timer_capture,
|
|
270
271
|
log_prefix: str = "",
|
|
272
|
+
engine_config=None,
|
|
271
273
|
) -> pd.DataFrame:
|
|
272
274
|
tables_used: Set[str] = set()
|
|
273
275
|
|
|
@@ -368,6 +370,10 @@ class DuckDBPro:
|
|
|
368
370
|
|
|
369
371
|
logger.debug(f"{log_prefix}[duckdb.pro] executing: {executing_query}")
|
|
370
372
|
|
|
373
|
+
# Re-apply live engine config (memory/threads/http/cache) so UI
|
|
374
|
+
# changes take effect on this persistent connection per query.
|
|
375
|
+
apply_runtime_pragmas(con, engine_config)
|
|
376
|
+
|
|
371
377
|
# Profiling PRAGMAs are connection-level state. Under concurrent
|
|
372
378
|
# queries the last SET wins — one query's profile may land in the
|
|
373
379
|
# wrong file. This is acceptable: profiling is best-effort
|
|
@@ -140,7 +140,11 @@ def _parse_memory_limit_mb(value: str) -> Optional[int]:
|
|
|
140
140
|
return int(amount)
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
def _derive_thread_count(
|
|
143
|
+
def _derive_thread_count(
|
|
144
|
+
memory_limit_str: str,
|
|
145
|
+
fallback: int = 2,
|
|
146
|
+
multiplier: Optional[float] = None,
|
|
147
|
+
) -> int:
|
|
144
148
|
"""Derive a safe DuckDB thread count for remote parquet reads.
|
|
145
149
|
|
|
146
150
|
DuckDB uses synchronous IO: each thread can make at most one HTTP/S3
|
|
@@ -170,9 +174,10 @@ def _derive_thread_count(memory_limit_str: str, fallback: int = 2) -> int:
|
|
|
170
174
|
return fallback
|
|
171
175
|
|
|
172
176
|
cpu_count = _os.cpu_count() or fallback
|
|
173
|
-
multiplier
|
|
177
|
+
if multiplier is None:
|
|
178
|
+
multiplier = settings.SUPERTABLE_DUCKDB_IO_MULTIPLIER
|
|
174
179
|
|
|
175
|
-
io_threads = cpu_count * multiplier
|
|
180
|
+
io_threads = int(cpu_count * multiplier)
|
|
176
181
|
# Safety ceiling: never allocate fewer than ~400 MB per thread.
|
|
177
182
|
memory_floor = max(1, mb // 400)
|
|
178
183
|
result = min(io_threads, memory_floor)
|
|
@@ -625,6 +630,56 @@ def init_connection(
|
|
|
625
630
|
pass
|
|
626
631
|
|
|
627
632
|
|
|
633
|
+
def apply_runtime_pragmas(con: duckdb.DuckDBPyConnection, cfg) -> None:
|
|
634
|
+
"""Re-apply the session-settable DuckDB pragmas from a live engine config.
|
|
635
|
+
|
|
636
|
+
DuckDB Lite/Pro reuse a persistent connection, so settings applied once at
|
|
637
|
+
``init_connection`` time would otherwise freeze for the connection's life.
|
|
638
|
+
Calling this immediately before each query makes the org's engine config
|
|
639
|
+
(memory limit, thread count, HTTP timeout, external file cache) take effect
|
|
640
|
+
live — the connection adopts the latest values without being torn down.
|
|
641
|
+
|
|
642
|
+
``cfg`` is an ``EngineRuntimeConfig``. When None the connection keeps
|
|
643
|
+
whatever ``init_connection`` applied (settings-based), so callers without a
|
|
644
|
+
resolved config are unaffected. Every pragma is best-effort: httpfs-only
|
|
645
|
+
settings silently no-op on connections that have not loaded httpfs.
|
|
646
|
+
"""
|
|
647
|
+
if cfg is None:
|
|
648
|
+
return
|
|
649
|
+
|
|
650
|
+
memory_limit = (cfg.duckdb_memory_limit or "").strip() or "1GB"
|
|
651
|
+
try:
|
|
652
|
+
con.execute(f"PRAGMA memory_limit='{memory_limit}';")
|
|
653
|
+
except Exception:
|
|
654
|
+
pass
|
|
655
|
+
|
|
656
|
+
# Explicit thread count wins; otherwise derive from the live memory limit
|
|
657
|
+
# and IO multiplier (same formula as init_connection).
|
|
658
|
+
if cfg.duckdb_threads is not None:
|
|
659
|
+
thread_count = cfg.duckdb_threads
|
|
660
|
+
else:
|
|
661
|
+
thread_count = _derive_thread_count(memory_limit, multiplier=cfg.duckdb_io_multiplier)
|
|
662
|
+
try:
|
|
663
|
+
con.execute(f"SET threads={thread_count};")
|
|
664
|
+
except Exception:
|
|
665
|
+
pass
|
|
666
|
+
|
|
667
|
+
# httpfs settings — only effective once httpfs is loaded (remote reads).
|
|
668
|
+
if cfg.duckdb_http_timeout is not None:
|
|
669
|
+
try:
|
|
670
|
+
con.execute(f"SET http_timeout={int(cfg.duckdb_http_timeout)};")
|
|
671
|
+
except Exception:
|
|
672
|
+
pass
|
|
673
|
+
|
|
674
|
+
cache_size = (cfg.duckdb_external_cache_size or "").strip()
|
|
675
|
+
if cache_size:
|
|
676
|
+
try:
|
|
677
|
+
con.execute("SET enable_external_file_cache=true;")
|
|
678
|
+
con.execute(f"SET external_file_cache_max_size='{sanitize_sql_string(cache_size)}';")
|
|
679
|
+
except Exception:
|
|
680
|
+
pass
|
|
681
|
+
|
|
682
|
+
|
|
628
683
|
# =========================================================
|
|
629
684
|
# RBAC view creation
|
|
630
685
|
# =========================================================
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# route: supertable.engine.engine_config
|
|
2
|
+
"""
|
|
3
|
+
Engine runtime configuration — single source of truth for resolution.
|
|
4
|
+
|
|
5
|
+
Engine config is stored once per organization at **system scope**
|
|
6
|
+
(``supertable:{org}:system:config:engine``). It is global to the org, not
|
|
7
|
+
per-supertable, and is resolved **live on every query** (no caching) so that
|
|
8
|
+
changes made in the UI take effect on the next query without a restart or a
|
|
9
|
+
connection reset.
|
|
10
|
+
|
|
11
|
+
Resolution precedence, per field::
|
|
12
|
+
|
|
13
|
+
Redis (org system config) → environment variable → built-in default
|
|
14
|
+
|
|
15
|
+
Two entry points share that precedence so the engine and the UI never disagree:
|
|
16
|
+
|
|
17
|
+
* :func:`resolve_engine_config` → typed :class:`EngineRuntimeConfig`,
|
|
18
|
+
consumed by the executor and the
|
|
19
|
+
DuckDB leaf executors.
|
|
20
|
+
* :func:`resolve_engine_config_provenance` → per-field ``{value, source,
|
|
21
|
+
env_var, default}`` dict for the
|
|
22
|
+
API / UI to display where each
|
|
23
|
+
effective value came from.
|
|
24
|
+
|
|
25
|
+
Built-in defaults mirror the :mod:`supertable.config.settings` dataclass
|
|
26
|
+
defaults, so behaviour is identical whether a value flows through ``settings``
|
|
27
|
+
(no Redis override) or through this resolver.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import os
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
from typing import Any, Dict, Optional, Tuple
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Canonical field spec: key → (env var, built-in default as string).
|
|
37
|
+
# Defaults mirror the settings dataclass defaults in
|
|
38
|
+
# supertable/config/settings.py — keep the two in sync.
|
|
39
|
+
_FIELD_SPEC: Dict[str, Tuple[str, str]] = {
|
|
40
|
+
"engine_lite_max_bytes": ("SUPERTABLE_ENGINE_LITE_MAX_BYTES", str(100 * 1024 * 1024)),
|
|
41
|
+
"engine_spark_min_bytes": ("SUPERTABLE_ENGINE_SPARK_MIN_BYTES", str(10 * 1024 * 1024 * 1024)),
|
|
42
|
+
"engine_freshness_sec": ("SUPERTABLE_ENGINE_FRESHNESS_SEC", "300"),
|
|
43
|
+
"duckdb_memory_limit": ("SUPERTABLE_DUCKDB_MEMORY_LIMIT", "1GB"),
|
|
44
|
+
"duckdb_io_multiplier": ("SUPERTABLE_DUCKDB_IO_MULTIPLIER", "3"),
|
|
45
|
+
"duckdb_threads": ("SUPERTABLE_DUCKDB_THREADS", ""),
|
|
46
|
+
"duckdb_http_timeout": ("SUPERTABLE_DUCKDB_HTTP_TIMEOUT", ""),
|
|
47
|
+
"duckdb_external_cache_size": ("SUPERTABLE_DUCKDB_EXTERNAL_CACHE_SIZE", "5GB"),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Public tuple of recognised field names (whitelist for callers/tests).
|
|
51
|
+
ENGINE_CONFIG_FIELDS: Tuple[str, ...] = tuple(_FIELD_SPEC.keys())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class EngineRuntimeConfig:
|
|
56
|
+
"""Effective, typed engine configuration for a single resolution."""
|
|
57
|
+
|
|
58
|
+
# auto-pick thresholds
|
|
59
|
+
engine_lite_max_bytes: int
|
|
60
|
+
engine_spark_min_bytes: int
|
|
61
|
+
engine_freshness_sec: int
|
|
62
|
+
|
|
63
|
+
# duckdb session pragmas (re-applied live per query)
|
|
64
|
+
duckdb_memory_limit: str # e.g. "1GB"
|
|
65
|
+
duckdb_io_multiplier: float
|
|
66
|
+
duckdb_threads: Optional[int] # None → auto-derive from memory limit
|
|
67
|
+
duckdb_http_timeout: Optional[int] # seconds; None → leave DuckDB default
|
|
68
|
+
duckdb_external_cache_size: str # "" → external file cache disabled
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _redis_cfg(org: str, catalog: Optional[Any]) -> Dict[str, Any]:
|
|
72
|
+
"""Read the stored engine config for ``org`` (best effort).
|
|
73
|
+
|
|
74
|
+
Returns an empty dict when no catalog is supplied or Redis is unreachable,
|
|
75
|
+
so the resolver degrades cleanly to env vars + defaults.
|
|
76
|
+
"""
|
|
77
|
+
if not (org and catalog is not None):
|
|
78
|
+
return {}
|
|
79
|
+
try:
|
|
80
|
+
return catalog.get_engine_config(org) or {}
|
|
81
|
+
except Exception:
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _effective(redis_cfg: Dict[str, Any], key: str) -> Tuple[str, str]:
|
|
86
|
+
"""Return ``(value, source)`` for one field. source ∈ redis|env|default."""
|
|
87
|
+
env_var, default = _FIELD_SPEC[key]
|
|
88
|
+
redis_val = redis_cfg.get(key)
|
|
89
|
+
if redis_val is not None and str(redis_val).strip() != "":
|
|
90
|
+
return str(redis_val).strip(), "redis"
|
|
91
|
+
env_val = (os.getenv(env_var) or "").strip()
|
|
92
|
+
if env_val:
|
|
93
|
+
return env_val, "env"
|
|
94
|
+
return default, "default"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _to_int(s: Any, fallback: int) -> int:
|
|
98
|
+
try:
|
|
99
|
+
return int(str(s).strip())
|
|
100
|
+
except (TypeError, ValueError):
|
|
101
|
+
return fallback
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _to_float(s: Any, fallback: float) -> float:
|
|
105
|
+
try:
|
|
106
|
+
return float(str(s).strip())
|
|
107
|
+
except (TypeError, ValueError):
|
|
108
|
+
return fallback
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def resolve_engine_config(org: str, catalog: Optional[Any] = None) -> EngineRuntimeConfig:
|
|
112
|
+
"""Resolve effective engine config for ``org`` (Redis → env → default).
|
|
113
|
+
|
|
114
|
+
``catalog`` is an optional ``RedisCatalog``. When it is None or Redis is
|
|
115
|
+
unreachable the resolver falls back to environment variables and built-in
|
|
116
|
+
defaults, which keeps the engine usable without Redis (e.g. in unit tests).
|
|
117
|
+
"""
|
|
118
|
+
cfg = _redis_cfg(org, catalog)
|
|
119
|
+
val = {k: _effective(cfg, k)[0] for k in _FIELD_SPEC}
|
|
120
|
+
|
|
121
|
+
threads = _to_int(val["duckdb_threads"], 0) if val["duckdb_threads"] else 0
|
|
122
|
+
http = _to_int(val["duckdb_http_timeout"], 0) if val["duckdb_http_timeout"] else 0
|
|
123
|
+
|
|
124
|
+
return EngineRuntimeConfig(
|
|
125
|
+
engine_lite_max_bytes=_to_int(val["engine_lite_max_bytes"], 100 * 1024 * 1024),
|
|
126
|
+
engine_spark_min_bytes=_to_int(val["engine_spark_min_bytes"], 10 * 1024 * 1024 * 1024),
|
|
127
|
+
engine_freshness_sec=_to_int(val["engine_freshness_sec"], 300),
|
|
128
|
+
duckdb_memory_limit=val["duckdb_memory_limit"].strip() or "1GB",
|
|
129
|
+
duckdb_io_multiplier=_to_float(val["duckdb_io_multiplier"], 3.0),
|
|
130
|
+
duckdb_threads=(threads if threads > 0 else None),
|
|
131
|
+
duckdb_http_timeout=(http if http > 0 else None),
|
|
132
|
+
duckdb_external_cache_size=val["duckdb_external_cache_size"].strip(),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def resolve_engine_config_provenance(org: str, catalog: Optional[Any] = None) -> Dict[str, Any]:
|
|
137
|
+
"""Per-field provenance for the UI: ``{value, source, env_var, default}``.
|
|
138
|
+
|
|
139
|
+
``source`` is one of ``redis`` / ``env`` / ``default``. The extra
|
|
140
|
+
``modified_ms`` key carries the last-write timestamp (or None).
|
|
141
|
+
"""
|
|
142
|
+
cfg = _redis_cfg(org, catalog)
|
|
143
|
+
result: Dict[str, Any] = {}
|
|
144
|
+
for key, (env_var, default) in _FIELD_SPEC.items():
|
|
145
|
+
value, source = _effective(cfg, key)
|
|
146
|
+
result[key] = {
|
|
147
|
+
"value": value,
|
|
148
|
+
"source": source,
|
|
149
|
+
"env_var": env_var,
|
|
150
|
+
"default": default,
|
|
151
|
+
}
|
|
152
|
+
result["modified_ms"] = cfg.get("modified_ms")
|
|
153
|
+
return result
|
|
@@ -16,9 +16,9 @@ from supertable.utils.sql_parser import SQLParser
|
|
|
16
16
|
from supertable.engine.engine_enum import Engine
|
|
17
17
|
from supertable.engine.duckdb_lite import DuckDBLite
|
|
18
18
|
from supertable.engine.duckdb_pro import DuckDBPro
|
|
19
|
+
from supertable.engine.engine_config import resolve_engine_config, EngineRuntimeConfig
|
|
19
20
|
from supertable.data_classes import Reflection
|
|
20
21
|
from supertable.config.defaults import logger
|
|
21
|
-
from supertable.config.settings import settings
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
# Module-level singleton for the pro executor so the persistent
|
|
@@ -46,8 +46,23 @@ class Executor:
|
|
|
46
46
|
self.organization = organization
|
|
47
47
|
self.lite_exec = DuckDBLite(storage=storage)
|
|
48
48
|
self.spark_exec = None
|
|
49
|
+
self._catalog = None # lazily created RedisCatalog for live config reads
|
|
49
50
|
|
|
50
|
-
def
|
|
51
|
+
def _get_catalog(self):
|
|
52
|
+
"""Lazily create a RedisCatalog for live engine-config reads.
|
|
53
|
+
|
|
54
|
+
Returns None when Redis is unreachable so config resolution degrades to
|
|
55
|
+
environment variables and built-in defaults instead of failing a query.
|
|
56
|
+
"""
|
|
57
|
+
if self._catalog is None:
|
|
58
|
+
try:
|
|
59
|
+
from supertable.redis_catalog import RedisCatalog
|
|
60
|
+
self._catalog = RedisCatalog()
|
|
61
|
+
except Exception:
|
|
62
|
+
self._catalog = False # sentinel: construction failed, do not retry
|
|
63
|
+
return self._catalog or None
|
|
64
|
+
|
|
65
|
+
def _auto_pick(self, reflection: Reflection, cfg: EngineRuntimeConfig) -> Engine:
|
|
51
66
|
"""Select the best engine based on data size and freshness.
|
|
52
67
|
|
|
53
68
|
Decision matrix (all thresholds configurable via env vars):
|
|
@@ -74,21 +89,10 @@ class Executor:
|
|
|
74
89
|
"""
|
|
75
90
|
bytes_total = reflection.reflection_bytes
|
|
76
91
|
|
|
77
|
-
# --- thresholds (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
lite_max = 100 * 1024 * 1024 # 100 MB
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
spark_min = settings.SUPERTABLE_ENGINE_SPARK_MIN_BYTES
|
|
85
|
-
except ValueError:
|
|
86
|
-
spark_min = 10 * 1024 * 1024 * 1024 # 10 GB
|
|
87
|
-
|
|
88
|
-
try:
|
|
89
|
-
freshness_threshold_s = settings.SUPERTABLE_ENGINE_FRESHNESS_SEC
|
|
90
|
-
except ValueError:
|
|
91
|
-
freshness_threshold_s = 300 # 5 min
|
|
92
|
+
# --- thresholds (resolved live from org system config) ---
|
|
93
|
+
lite_max = cfg.engine_lite_max_bytes
|
|
94
|
+
spark_min = cfg.engine_spark_min_bytes
|
|
95
|
+
freshness_threshold_s = cfg.engine_freshness_sec
|
|
92
96
|
|
|
93
97
|
# --- freshness: how long ago was the most recent snapshot updated ---
|
|
94
98
|
if reflection.freshness_ms > 0:
|
|
@@ -138,7 +142,11 @@ class Executor:
|
|
|
138
142
|
plan_stats: PlanStats,
|
|
139
143
|
log_prefix: str,
|
|
140
144
|
) -> Tuple[pd.DataFrame, str]:
|
|
141
|
-
|
|
145
|
+
# Resolve engine config live (Redis → env → default) for this query so
|
|
146
|
+
# UI changes take effect immediately without restart or cache.
|
|
147
|
+
cfg = resolve_engine_config(self.organization, self._get_catalog())
|
|
148
|
+
|
|
149
|
+
chosen = engine if engine != Engine.AUTO else self._auto_pick(reflection, cfg)
|
|
142
150
|
|
|
143
151
|
def timer_capture(evt: str):
|
|
144
152
|
timer.capture_and_reset_timing(evt)
|
|
@@ -150,6 +158,7 @@ class Executor:
|
|
|
150
158
|
query_manager=query_manager,
|
|
151
159
|
timer_capture=timer_capture,
|
|
152
160
|
log_prefix=log_prefix,
|
|
161
|
+
engine_config=cfg,
|
|
153
162
|
)
|
|
154
163
|
used = "duckdb_lite"
|
|
155
164
|
|
|
@@ -161,6 +170,7 @@ class Executor:
|
|
|
161
170
|
query_manager=query_manager,
|
|
162
171
|
timer_capture=timer_capture,
|
|
163
172
|
log_prefix=log_prefix,
|
|
173
|
+
engine_config=cfg,
|
|
164
174
|
)
|
|
165
175
|
used = "duckdb_pro"
|
|
166
176
|
|
|
@@ -1525,6 +1525,20 @@ return 1
|
|
|
1525
1525
|
return []
|
|
1526
1526
|
|
|
1527
1527
|
|
|
1528
|
+
def delete_spark_cluster(self, org: str, cluster_id: str) -> bool:
|
|
1529
|
+
"""Remove a registered Spark Thrift cluster. Returns True if it existed."""
|
|
1530
|
+
if not (org and cluster_id):
|
|
1531
|
+
return False
|
|
1532
|
+
try:
|
|
1533
|
+
removed = self.r.hdel(RK.spark_thrifts(org), cluster_id)
|
|
1534
|
+
if removed:
|
|
1535
|
+
logger.info(f"[redis-catalog] spark thrift deleted: {org}/{cluster_id}")
|
|
1536
|
+
return bool(removed)
|
|
1537
|
+
except redis.RedisError as e:
|
|
1538
|
+
logger.error(f"[redis-catalog] delete_spark_cluster error: {e}")
|
|
1539
|
+
return False
|
|
1540
|
+
|
|
1541
|
+
|
|
1528
1542
|
def select_spark_cluster(self, org: str, job_bytes: int, force: bool = False) -> Optional[Dict[str, Any]]:
|
|
1529
1543
|
"""
|
|
1530
1544
|
Select the best active Spark Thrift cluster for a job of the given size.
|
|
@@ -1669,18 +1683,20 @@ return 1
|
|
|
1669
1683
|
def set_engine_config(
|
|
1670
1684
|
self,
|
|
1671
1685
|
org: str,
|
|
1672
|
-
sup: str,
|
|
1673
1686
|
config: Dict[str, Any],
|
|
1674
1687
|
) -> bool:
|
|
1675
1688
|
"""Store engine runtime configuration (DuckDB + auto-pick thresholds).
|
|
1676
1689
|
|
|
1690
|
+
Org-level system scope: one engine config per organization, applied
|
|
1691
|
+
globally across all supertables (not per-supertable).
|
|
1692
|
+
|
|
1677
1693
|
The config dict is stored as a JSON string. Only recognised fields
|
|
1678
1694
|
(see ENGINE_CONFIG_FIELDS) are persisted — unknown keys are silently
|
|
1679
1695
|
dropped to prevent injection of arbitrary settings.
|
|
1680
1696
|
|
|
1681
1697
|
Existing config is fully replaced (last-write-wins).
|
|
1682
1698
|
"""
|
|
1683
|
-
if not
|
|
1699
|
+
if not org:
|
|
1684
1700
|
return False
|
|
1685
1701
|
try:
|
|
1686
1702
|
# Whitelist recognised fields only.
|
|
@@ -1691,7 +1707,7 @@ return 1
|
|
|
1691
1707
|
}
|
|
1692
1708
|
doc["modified_ms"] = _now_ms()
|
|
1693
1709
|
self.r.set(
|
|
1694
|
-
RK.config_engine(org
|
|
1710
|
+
RK.config_engine(org),
|
|
1695
1711
|
json.dumps(doc, default=str),
|
|
1696
1712
|
)
|
|
1697
1713
|
return True
|
|
@@ -1702,16 +1718,15 @@ return 1
|
|
|
1702
1718
|
def get_engine_config(
|
|
1703
1719
|
self,
|
|
1704
1720
|
org: str,
|
|
1705
|
-
sup: str,
|
|
1706
1721
|
) -> Optional[Dict[str, Any]]:
|
|
1707
|
-
"""Retrieve engine runtime configuration.
|
|
1722
|
+
"""Retrieve engine runtime configuration (org-level system scope).
|
|
1708
1723
|
|
|
1709
|
-
Returns None if no config has been stored for this
|
|
1724
|
+
Returns None if no config has been stored for this organization.
|
|
1710
1725
|
"""
|
|
1711
|
-
if not
|
|
1726
|
+
if not org:
|
|
1712
1727
|
return None
|
|
1713
1728
|
try:
|
|
1714
|
-
raw = self.r.get(RK.config_engine(org
|
|
1729
|
+
raw = self.r.get(RK.config_engine(org))
|
|
1715
1730
|
if raw:
|
|
1716
1731
|
return json.loads(raw)
|
|
1717
1732
|
except (redis.RedisError, json.JSONDecodeError) as e:
|
|
@@ -38,6 +38,7 @@ Hierarchy (v2)
|
|
|
38
38
|
spark:
|
|
39
39
|
thrifts HASH Spark Thrift clusters
|
|
40
40
|
plugs HASH Spark Plug runtimes
|
|
41
|
+
config:engine STRING engine runtime config
|
|
41
42
|
lakes: ← user-data sentinel
|
|
42
43
|
{sup}: ← supertable scope
|
|
43
44
|
meta:
|
|
@@ -55,7 +56,6 @@ Hierarchy (v2)
|
|
|
55
56
|
pipes:
|
|
56
57
|
index SET pipe names
|
|
57
58
|
doc:{pipe_name} STRING pipe definition
|
|
58
|
-
config:engine STRING engine runtime config
|
|
59
59
|
lock:
|
|
60
60
|
leaf:
|
|
61
61
|
doc:{simple} STRING per-table lock token
|
|
@@ -572,12 +572,9 @@ def pipe_pattern(org: str, sup: str, staging_name: str) -> str:
|
|
|
572
572
|
|
|
573
573
|
# --- Engine config --------------------------------------------------------- #
|
|
574
574
|
|
|
575
|
-
def config_engine(org: str
|
|
576
|
-
"""Engine runtime config (STRING)."""
|
|
577
|
-
return (
|
|
578
|
-
f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{LAKES_SCOPE}"
|
|
579
|
-
f":{_safe('sup', sup)}:config:engine"
|
|
580
|
-
)
|
|
575
|
+
def config_engine(org: str) -> str:
|
|
576
|
+
"""Engine runtime config (STRING). Org-level system scope (global, not per-supertable)."""
|
|
577
|
+
return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:config:engine"
|
|
581
578
|
|
|
582
579
|
|
|
583
580
|
# --- Locks ----------------------------------------------------------------- #
|
|
@@ -70,6 +70,7 @@ def _all_helpers() -> list[tuple[str, str, str]]:
|
|
|
70
70
|
("share_index", RK.share_index(ORG), f"{sys_pre}:shares:index"),
|
|
71
71
|
("spark_thrifts", RK.spark_thrifts(ORG), f"{sys_pre}:spark:thrifts"),
|
|
72
72
|
("spark_plugs", RK.spark_plugs(ORG), f"{sys_pre}:spark:plugs"),
|
|
73
|
+
("config_engine", RK.config_engine(ORG), f"{sys_pre}:config:engine"),
|
|
73
74
|
|
|
74
75
|
# ---- Lakes scope (per-org supertable enumeration) ---------------
|
|
75
76
|
("lakes_scope", RK.lakes_scope(ORG), f"supertable:{ORG}:lakes"),
|
|
@@ -95,9 +96,6 @@ def _all_helpers() -> list[tuple[str, str, str]]:
|
|
|
95
96
|
("pipe_doc", RK.pipe_doc(ORG, SUP, STAGING, PIPE), f"{lake_pre}:meta:staging:doc:{STAGING}:pipes:doc:{PIPE}"),
|
|
96
97
|
("pipe_pattern", RK.pipe_pattern(ORG, SUP, STAGING), f"{lake_pre}:meta:staging:doc:{STAGING}:pipes:doc:*"),
|
|
97
98
|
|
|
98
|
-
# ---- Config -----------------------------------------------------
|
|
99
|
-
("config_engine", RK.config_engine(ORG, SUP), f"{lake_pre}:config:engine"),
|
|
100
|
-
|
|
101
99
|
# ---- Locks ------------------------------------------------------
|
|
102
100
|
("lock_leaf", RK.lock_leaf(ORG, SUP, SIMPLE), f"{lake_pre}:lock:leaf:doc:{SIMPLE}"),
|
|
103
101
|
("lock_leaf_pattern", RK.lock_leaf_pattern(ORG, SUP), f"{lake_pre}:lock:leaf:doc:*"),
|
|
@@ -99,6 +99,7 @@ supertable/engine/data_estimator.py
|
|
|
99
99
|
supertable/engine/duckdb_lite.py
|
|
100
100
|
supertable/engine/duckdb_pro.py
|
|
101
101
|
supertable/engine/engine_common.py
|
|
102
|
+
supertable/engine/engine_config.py
|
|
102
103
|
supertable/engine/engine_enum.py
|
|
103
104
|
supertable/engine/executor.py
|
|
104
105
|
supertable/engine/plan_stats.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|