supertable 2.1.1__tar.gz → 2.2.1__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.1.1/supertable.egg-info → supertable-2.2.1}/PKG-INFO +1 -1
- {supertable-2.1.1 → supertable-2.2.1}/pyproject.toml +1 -1
- {supertable-2.1.1 → supertable-2.2.1}/setup.py +1 -1
- {supertable-2.1.1 → supertable-2.2.1}/supertable/__init__.py +9 -1
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/settings.py +22 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/data_reader.py +65 -1
- supertable-2.2.1/supertable/data_writer.py +1236 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/defaults.py +1 -1
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/data_estimator.py +9 -1
- supertable-2.2.1/supertable/errors.py +75 -0
- supertable-2.2.1/supertable/gc/__init__.py +26 -0
- supertable-2.2.1/supertable/gc/cleaner.py +258 -0
- supertable-2.2.1/supertable/gc/daemon.py +284 -0
- supertable-2.2.1/supertable/gc/queue.py +221 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/meta_reader.py +34 -14
- supertable-2.2.1/supertable/monitoring/__init__.py +62 -0
- supertable-2.2.1/supertable/monitoring/partitions.py +605 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/monitoring_writer.py +39 -11
- {supertable-2.1.1 → supertable-2.2.1}/supertable/plan_extender.py +37 -10
- {supertable-2.1.1 → supertable-2.2.1}/supertable/processing.py +147 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/role_manager.py +15 -16
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_rbac.py +136 -8
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/user_manager.py +9 -1
- {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_catalog.py +110 -8
- {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_keys.py +179 -17
- {supertable-2.1.1 → supertable-2.2.1}/supertable/simple_table.py +57 -11
- {supertable-2.1.1 → supertable-2.2.1}/supertable/super_table.py +27 -1
- supertable-2.2.1/supertable/tests/test_create_if_missing.py +267 -0
- supertable-2.2.1/supertable/tests/test_data_reader_preflight.py +286 -0
- supertable-2.2.1/supertable/tests/test_data_writer_compact.py +1177 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer_tombstones.py +115 -0
- supertable-2.2.1/supertable/tests/test_errors.py +81 -0
- supertable-2.2.1/supertable/tests/test_gc_cleaner.py +330 -0
- supertable-2.2.1/supertable/tests/test_gc_daemon.py +251 -0
- supertable-2.2.1/supertable/tests/test_gc_queue.py +331 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_meta_reader.py +9 -1
- supertable-2.2.1/supertable/tests/test_monitoring_partitions.py +756 -0
- supertable-2.2.1/supertable/tests/test_monitoring_sink_guard.py +167 -0
- supertable-2.2.1/supertable/tests/test_processing_compact_resources.py +575 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_redis_key_prefix.py +86 -14
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_simple_table.py +98 -0
- {supertable-2.1.1 → supertable-2.2.1/supertable.egg-info}/PKG-INFO +1 -1
- {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/SOURCES.txt +17 -0
- supertable-2.1.1/supertable/data_writer.py +0 -609
- {supertable-2.1.1 → supertable-2.2.1}/LICENSE +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/README.md +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/requirements.txt +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/setup.cfg +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/admin.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/chain.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/consumers.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/crypto.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/events.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/export.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/logger.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/middleware.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/reader.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/retention.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_chain.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_crypto.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_emit.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_events.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_retention.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/writer_parquet.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/writer_redis.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/defaults.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/homedir.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_defaults.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_homedir.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_settings.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/data_classes.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/__main__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/check_filter_builder.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/controller.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/data_writer_helpers.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/dummy_data.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/read_parquet_header.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_01_01_create_super_table.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_01_02_enable_mirroring_formats.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_02_create_roles.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_03_create_users.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_01_write_dummy_data.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_02_write_single_data.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_03_01_write_staging.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_03_02_create_pipe.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_04_01_write_monitoring_simple.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_04_02_write_monitoring_parallel.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_05_write_tombstone.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_01_read_data_error.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_02_01_read_super_data_ok.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_02_02_read_table_data_ok.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_03_read_meta.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_04_read_staging.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_06_01_read_roles.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_06_02_read_user.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_07_01_estimate_read.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_07_02_estimate_files.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_08_read_snapshot_history.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s04_01_03_delete_pipe.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s05_01_delete_table.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s05_02_delete_super_table.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/core.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/defaults.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/generate.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/load.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/topup.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/duckdb_lite.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/duckdb_pro.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/engine_common.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/engine_enum.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/executor.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/plan_stats.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/spark_thrift.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/conftest.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/test_dedup_read.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/test_engine.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/benchmark_locking.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/measure_lock_speed.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/measure_lock_time.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/file_lock.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/redis_lock.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/test_file_lock.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/test_redis_lock.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/logging.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_delta.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_formats.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_iceberg.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_parquet.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/query_plan_manager.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/access_control.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/filter_builder.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/permissions.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/row_column_security.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_filter_builder.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_rbac_per_table.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_connector.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_infra.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/staging_area.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/azure_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/gcp_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/local_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/minio_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/s3_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/storage_factory.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/storage_interface.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/tests/test_storage.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/super_pipe.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_align_to_schema_fix.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_reader.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer_comprehensive.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_dedup_on_read_write.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_newer_than.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_process_delete_only.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_processing.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_query_sql.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_small_file_compaction.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_super_table.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_supertable_all.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/__init__.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/helper.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/sql_parser.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/tests/test_sql_parser_columns.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/timer.py +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/dependency_links.txt +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/entry_points.txt +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/requires.txt +0 -0
- {supertable-2.1.1 → supertable-2.2.1}/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.
|
|
22
|
+
version="2.2.1",
|
|
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.
|
|
28
|
+
__version__ = "2.2.1"
|
|
29
29
|
|
|
30
30
|
# Re-export the core public surface so users can do ``from supertable import …``
|
|
31
31
|
# instead of remembering submodule paths.
|
|
@@ -39,6 +39,11 @@ from supertable.super_pipe import SuperPipe
|
|
|
39
39
|
from supertable.redis_catalog import RedisCatalog
|
|
40
40
|
from supertable.rbac.role_manager import RoleManager
|
|
41
41
|
from supertable.rbac.user_manager import UserManager
|
|
42
|
+
from supertable.errors import (
|
|
43
|
+
SupertableLookupError,
|
|
44
|
+
SuperTableNotFoundError,
|
|
45
|
+
TableNotFoundError,
|
|
46
|
+
)
|
|
42
47
|
|
|
43
48
|
__all__ = [
|
|
44
49
|
"__version__",
|
|
@@ -55,4 +60,7 @@ __all__ = [
|
|
|
55
60
|
"RedisCatalog",
|
|
56
61
|
"RoleManager",
|
|
57
62
|
"UserManager",
|
|
63
|
+
"SupertableLookupError",
|
|
64
|
+
"SuperTableNotFoundError",
|
|
65
|
+
"TableNotFoundError",
|
|
58
66
|
]
|
|
@@ -282,6 +282,21 @@ class Settings:
|
|
|
282
282
|
SUPERTABLE_SHARE_PRESIGN_TTL: int = 14400 # SUPERTABLE_SHARE_PRESIGN_TTL (seconds, default 4h)
|
|
283
283
|
SUPERTABLE_SHARE_REFRESH_BUFFER: int = 600 # SUPERTABLE_SHARE_REFRESH_BUFFER (seconds, refresh 10min before expiry)
|
|
284
284
|
|
|
285
|
+
# ── Storage GC (deferred deletion of sunset parquets + old snapshot JSONs) ──
|
|
286
|
+
# Sunset parquet files and old snapshot JSONs are never deleted by the
|
|
287
|
+
# writer directly. When either flag is enabled, the writer XADDs paths
|
|
288
|
+
# to a per-table Redis STREAM after a successful leaf-CAS. A separate
|
|
289
|
+
# cleaner daemon (``python -m supertable.gc.cleaner``) drains entries
|
|
290
|
+
# older than ``SUPERTABLE_GC_DELAY_SEC`` and calls ``storage.delete()``.
|
|
291
|
+
# The delay window avoids the race where an in-flight reader's
|
|
292
|
+
# ``parquet_scan([...])`` resolves a file path right before the writer
|
|
293
|
+
# deletes it. Defaults below preserve today's behaviour (off, unlimited).
|
|
294
|
+
SUPERTABLE_SNAPSHOT_RETENTION: int = 0 # SUPERTABLE_SNAPSHOT_RETENTION (0 = keep all snapshot JSONs)
|
|
295
|
+
SUPERTABLE_SUNSET_GC_ENABLED: bool = False # SUPERTABLE_SUNSET_GC_ENABLED (delete sunset parquets via queue)
|
|
296
|
+
SUPERTABLE_GC_DELAY_SEC: int = 1800 # SUPERTABLE_GC_DELAY_SEC (min age before cleaner deletes)
|
|
297
|
+
SUPERTABLE_GC_SLEEP_SEC: int = 60 # SUPERTABLE_GC_SLEEP_SEC (cleaner loop sleep)
|
|
298
|
+
SUPERTABLE_GC_BATCH_SIZE: int = 500 # SUPERTABLE_GC_BATCH_SIZE (max entries processed per table per tick)
|
|
299
|
+
|
|
285
300
|
|
|
286
301
|
# ── Convenience properties ───────────────────────────────────────
|
|
287
302
|
|
|
@@ -528,6 +543,13 @@ def _build_settings() -> Settings:
|
|
|
528
543
|
# Data Sharing
|
|
529
544
|
SUPERTABLE_SHARE_PRESIGN_TTL=_env_int("SUPERTABLE_SHARE_PRESIGN_TTL", 14400),
|
|
530
545
|
SUPERTABLE_SHARE_REFRESH_BUFFER=_env_int("SUPERTABLE_SHARE_REFRESH_BUFFER", 600),
|
|
546
|
+
|
|
547
|
+
# ── Storage GC ───────────────────────────────────────────────
|
|
548
|
+
SUPERTABLE_SNAPSHOT_RETENTION=_env_int("SUPERTABLE_SNAPSHOT_RETENTION", 0),
|
|
549
|
+
SUPERTABLE_SUNSET_GC_ENABLED=_env_bool("SUPERTABLE_SUNSET_GC_ENABLED", False),
|
|
550
|
+
SUPERTABLE_GC_DELAY_SEC=_env_int("SUPERTABLE_GC_DELAY_SEC", 1800),
|
|
551
|
+
SUPERTABLE_GC_SLEEP_SEC=_env_int("SUPERTABLE_GC_SLEEP_SEC", 60),
|
|
552
|
+
SUPERTABLE_GC_BATCH_SIZE=_env_int("SUPERTABLE_GC_BATCH_SIZE", 500),
|
|
531
553
|
)
|
|
532
554
|
|
|
533
555
|
|
|
@@ -10,6 +10,7 @@ from typing import Optional, Tuple, Any, List, Dict
|
|
|
10
10
|
import pandas as pd
|
|
11
11
|
|
|
12
12
|
from supertable.config.defaults import logger
|
|
13
|
+
from supertable.errors import SuperTableNotFoundError, TableNotFoundError
|
|
13
14
|
from supertable.storage.storage_factory import get_storage
|
|
14
15
|
from supertable.storage.storage_interface import StorageInterface
|
|
15
16
|
from supertable.utils.timer import Timer
|
|
@@ -59,6 +60,44 @@ class DataReader:
|
|
|
59
60
|
def _lp(self, msg: str) -> str:
|
|
60
61
|
return f"{self._log_ctx}{msg}"
|
|
61
62
|
|
|
63
|
+
def _assert_targets_exist(self, physical_tables) -> None:
|
|
64
|
+
"""Fail fast if any referenced (super, simple) is missing in Redis.
|
|
65
|
+
|
|
66
|
+
The read path must never create catalog entries as a side effect
|
|
67
|
+
of resolving a query. ``SuperTable`` / ``SimpleTable``
|
|
68
|
+
constructors used to do exactly that for callers that didn't pass
|
|
69
|
+
``create_if_missing=False`` — this guard is the SDK-level
|
|
70
|
+
invariant that says "reads cannot mint tables".
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
SuperTableNotFoundError: when the supertable's
|
|
74
|
+
``meta:root`` pointer is missing.
|
|
75
|
+
TableNotFoundError: when the simple table's
|
|
76
|
+
``meta:leaf:doc:{simple}`` pointer is missing.
|
|
77
|
+
"""
|
|
78
|
+
if not physical_tables:
|
|
79
|
+
return
|
|
80
|
+
# One catalog handle for the whole loop — cheaper than letting
|
|
81
|
+
# each .exists() call open a fresh connection.
|
|
82
|
+
catalog = RedisCatalog()
|
|
83
|
+
# Dedup by (super, simple) — SQL may mention the same table
|
|
84
|
+
# multiple times via different aliases.
|
|
85
|
+
seen = set()
|
|
86
|
+
for td in physical_tables:
|
|
87
|
+
super_name = td.super_name
|
|
88
|
+
simple_name = td.simple_name
|
|
89
|
+
if not super_name or not simple_name:
|
|
90
|
+
continue
|
|
91
|
+
key = (super_name, simple_name)
|
|
92
|
+
if key in seen:
|
|
93
|
+
continue
|
|
94
|
+
seen.add(key)
|
|
95
|
+
if not catalog.root_exists(self.organization, super_name):
|
|
96
|
+
raise SuperTableNotFoundError(self.organization, super_name)
|
|
97
|
+
if not catalog.leaf_exists(self.organization, super_name, simple_name):
|
|
98
|
+
raise TableNotFoundError(
|
|
99
|
+
self.organization, super_name, simple_name
|
|
100
|
+
)
|
|
62
101
|
|
|
63
102
|
def execute(
|
|
64
103
|
self,
|
|
@@ -80,7 +119,32 @@ class DataReader:
|
|
|
80
119
|
tables = parser.get_table_tuples()
|
|
81
120
|
physical_tables = parser.get_physical_tables()
|
|
82
121
|
|
|
83
|
-
#
|
|
122
|
+
# Read-path policy: reads never create. Verify every referenced
|
|
123
|
+
# (super, simple) exists in the Redis catalog **before** anything
|
|
124
|
+
# downstream — RBAC, estimator, or the executor — gets a chance
|
|
125
|
+
# to side-effect-bootstrap them.
|
|
126
|
+
#
|
|
127
|
+
# ORDERING MATTERS: ``restrict_read_access`` (called next) builds
|
|
128
|
+
# ``RoleManager(super_name=..., organization=...)`` which boots
|
|
129
|
+
# RBAC role storage in Redis for the supertable if it doesn't
|
|
130
|
+
# exist. Running the RBAC check first against a missing
|
|
131
|
+
# supertable would silently mint the RBAC scaffold before our
|
|
132
|
+
# existence check fires. Pre-flight FIRST.
|
|
133
|
+
#
|
|
134
|
+
# The check runs in its own try block so SuperTable/TableNotFound
|
|
135
|
+
# convert to the standard (empty_df, Status.ERROR, message)
|
|
136
|
+
# return — we don't want to raise these into the caller, but we
|
|
137
|
+
# DO want to keep ``restrict_read_access``'s PermissionError
|
|
138
|
+
# raising naturally (legacy behaviour API layers depend on for
|
|
139
|
+
# 403 translation).
|
|
140
|
+
try:
|
|
141
|
+
self._assert_targets_exist(physical_tables)
|
|
142
|
+
except (SuperTableNotFoundError, TableNotFoundError) as e:
|
|
143
|
+
logger.warning(self._lp(f"target missing: {e}"))
|
|
144
|
+
return pd.DataFrame(), Status.ERROR, str(e)
|
|
145
|
+
|
|
146
|
+
# RBAC check — also returns per-alias column/row filter definitions.
|
|
147
|
+
# PermissionError propagates to the caller (legacy behaviour).
|
|
84
148
|
rbac_views = restrict_read_access(
|
|
85
149
|
super_name=self.super_name,
|
|
86
150
|
organization=self.organization,
|