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.
Files changed (184) hide show
  1. {supertable-2.1.1/supertable.egg-info → supertable-2.2.1}/PKG-INFO +1 -1
  2. {supertable-2.1.1 → supertable-2.2.1}/pyproject.toml +1 -1
  3. {supertable-2.1.1 → supertable-2.2.1}/setup.py +1 -1
  4. {supertable-2.1.1 → supertable-2.2.1}/supertable/__init__.py +9 -1
  5. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/settings.py +22 -0
  6. {supertable-2.1.1 → supertable-2.2.1}/supertable/data_reader.py +65 -1
  7. supertable-2.2.1/supertable/data_writer.py +1236 -0
  8. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/defaults.py +1 -1
  9. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/data_estimator.py +9 -1
  10. supertable-2.2.1/supertable/errors.py +75 -0
  11. supertable-2.2.1/supertable/gc/__init__.py +26 -0
  12. supertable-2.2.1/supertable/gc/cleaner.py +258 -0
  13. supertable-2.2.1/supertable/gc/daemon.py +284 -0
  14. supertable-2.2.1/supertable/gc/queue.py +221 -0
  15. {supertable-2.1.1 → supertable-2.2.1}/supertable/meta_reader.py +34 -14
  16. supertable-2.2.1/supertable/monitoring/__init__.py +62 -0
  17. supertable-2.2.1/supertable/monitoring/partitions.py +605 -0
  18. {supertable-2.1.1 → supertable-2.2.1}/supertable/monitoring_writer.py +39 -11
  19. {supertable-2.1.1 → supertable-2.2.1}/supertable/plan_extender.py +37 -10
  20. {supertable-2.1.1 → supertable-2.2.1}/supertable/processing.py +147 -0
  21. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/role_manager.py +15 -16
  22. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_rbac.py +136 -8
  23. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/user_manager.py +9 -1
  24. {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_catalog.py +110 -8
  25. {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_keys.py +179 -17
  26. {supertable-2.1.1 → supertable-2.2.1}/supertable/simple_table.py +57 -11
  27. {supertable-2.1.1 → supertable-2.2.1}/supertable/super_table.py +27 -1
  28. supertable-2.2.1/supertable/tests/test_create_if_missing.py +267 -0
  29. supertable-2.2.1/supertable/tests/test_data_reader_preflight.py +286 -0
  30. supertable-2.2.1/supertable/tests/test_data_writer_compact.py +1177 -0
  31. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer_tombstones.py +115 -0
  32. supertable-2.2.1/supertable/tests/test_errors.py +81 -0
  33. supertable-2.2.1/supertable/tests/test_gc_cleaner.py +330 -0
  34. supertable-2.2.1/supertable/tests/test_gc_daemon.py +251 -0
  35. supertable-2.2.1/supertable/tests/test_gc_queue.py +331 -0
  36. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_meta_reader.py +9 -1
  37. supertable-2.2.1/supertable/tests/test_monitoring_partitions.py +756 -0
  38. supertable-2.2.1/supertable/tests/test_monitoring_sink_guard.py +167 -0
  39. supertable-2.2.1/supertable/tests/test_processing_compact_resources.py +575 -0
  40. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_redis_key_prefix.py +86 -14
  41. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_simple_table.py +98 -0
  42. {supertable-2.1.1 → supertable-2.2.1/supertable.egg-info}/PKG-INFO +1 -1
  43. {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/SOURCES.txt +17 -0
  44. supertable-2.1.1/supertable/data_writer.py +0 -609
  45. {supertable-2.1.1 → supertable-2.2.1}/LICENSE +0 -0
  46. {supertable-2.1.1 → supertable-2.2.1}/README.md +0 -0
  47. {supertable-2.1.1 → supertable-2.2.1}/requirements.txt +0 -0
  48. {supertable-2.1.1 → supertable-2.2.1}/setup.cfg +0 -0
  49. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/__init__.py +0 -0
  50. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/admin.py +0 -0
  51. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/chain.py +0 -0
  52. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/consumers.py +0 -0
  53. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/crypto.py +0 -0
  54. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/events.py +0 -0
  55. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/export.py +0 -0
  56. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/logger.py +0 -0
  57. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/middleware.py +0 -0
  58. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/reader.py +0 -0
  59. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/retention.py +0 -0
  60. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/__init__.py +0 -0
  61. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_chain.py +0 -0
  62. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_crypto.py +0 -0
  63. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_emit.py +0 -0
  64. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_events.py +0 -0
  65. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/tests/test_retention.py +0 -0
  66. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/writer_parquet.py +0 -0
  67. {supertable-2.1.1 → supertable-2.2.1}/supertable/audit/writer_redis.py +0 -0
  68. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/__init__.py +0 -0
  69. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/defaults.py +0 -0
  70. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/homedir.py +0 -0
  71. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/__init__.py +0 -0
  72. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_defaults.py +0 -0
  73. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_homedir.py +0 -0
  74. {supertable-2.1.1 → supertable-2.2.1}/supertable/config/tests/test_settings.py +0 -0
  75. {supertable-2.1.1 → supertable-2.2.1}/supertable/data_classes.py +0 -0
  76. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/__init__.py +0 -0
  77. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/__init__.py +0 -0
  78. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/__main__.py +0 -0
  79. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/check_filter_builder.py +0 -0
  80. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/controller.py +0 -0
  81. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/data_writer_helpers.py +0 -0
  82. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/dummy_data.py +0 -0
  83. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/read_parquet_header.py +0 -0
  84. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_01_01_create_super_table.py +0 -0
  85. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_01_02_enable_mirroring_formats.py +0 -0
  86. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_02_create_roles.py +0 -0
  87. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s01_03_create_users.py +0 -0
  88. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_01_write_dummy_data.py +0 -0
  89. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_02_write_single_data.py +0 -0
  90. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_03_01_write_staging.py +0 -0
  91. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_03_02_create_pipe.py +0 -0
  92. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_04_01_write_monitoring_simple.py +0 -0
  93. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_04_02_write_monitoring_parallel.py +0 -0
  94. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s02_05_write_tombstone.py +0 -0
  95. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_01_read_data_error.py +0 -0
  96. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_02_01_read_super_data_ok.py +0 -0
  97. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_02_02_read_table_data_ok.py +0 -0
  98. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_03_read_meta.py +0 -0
  99. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_04_read_staging.py +0 -0
  100. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_06_01_read_roles.py +0 -0
  101. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_06_02_read_user.py +0 -0
  102. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_07_01_estimate_read.py +0 -0
  103. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_07_02_estimate_files.py +0 -0
  104. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s03_08_read_snapshot_history.py +0 -0
  105. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s04_01_03_delete_pipe.py +0 -0
  106. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s05_01_delete_table.py +0 -0
  107. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/quickstart/s05_02_delete_super_table.py +0 -0
  108. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/__init__.py +0 -0
  109. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/core.py +0 -0
  110. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/defaults.py +0 -0
  111. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/generate.py +0 -0
  112. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/load.py +0 -0
  113. {supertable-2.1.1 → supertable-2.2.1}/supertable/demo/webshop/topup.py +0 -0
  114. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/__init__.py +0 -0
  115. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/duckdb_lite.py +0 -0
  116. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/duckdb_pro.py +0 -0
  117. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/engine_common.py +0 -0
  118. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/engine_enum.py +0 -0
  119. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/executor.py +0 -0
  120. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/plan_stats.py +0 -0
  121. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/spark_thrift.py +0 -0
  122. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/__init__.py +0 -0
  123. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/conftest.py +0 -0
  124. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/test_dedup_read.py +0 -0
  125. {supertable-2.1.1 → supertable-2.2.1}/supertable/engine/tests/test_engine.py +0 -0
  126. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/__init__.py +0 -0
  127. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/__init__.py +0 -0
  128. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/benchmark_locking.py +0 -0
  129. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/measure_lock_speed.py +0 -0
  130. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/benchmarks/measure_lock_time.py +0 -0
  131. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/file_lock.py +0 -0
  132. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/redis_lock.py +0 -0
  133. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/__init__.py +0 -0
  134. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/test_file_lock.py +0 -0
  135. {supertable-2.1.1 → supertable-2.2.1}/supertable/locking/tests/test_redis_lock.py +0 -0
  136. {supertable-2.1.1 → supertable-2.2.1}/supertable/logging.py +0 -0
  137. {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/__init__.py +0 -0
  138. {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_delta.py +0 -0
  139. {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_formats.py +0 -0
  140. {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_iceberg.py +0 -0
  141. {supertable-2.1.1 → supertable-2.2.1}/supertable/mirroring/mirror_parquet.py +0 -0
  142. {supertable-2.1.1 → supertable-2.2.1}/supertable/query_plan_manager.py +0 -0
  143. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/__init__.py +0 -0
  144. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/access_control.py +0 -0
  145. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/filter_builder.py +0 -0
  146. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/permissions.py +0 -0
  147. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/row_column_security.py +0 -0
  148. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_filter_builder.py +0 -0
  149. {supertable-2.1.1 → supertable-2.2.1}/supertable/rbac/tests/test_rbac_per_table.py +0 -0
  150. {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_connector.py +0 -0
  151. {supertable-2.1.1 → supertable-2.2.1}/supertable/redis_infra.py +0 -0
  152. {supertable-2.1.1 → supertable-2.2.1}/supertable/staging_area.py +0 -0
  153. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/__init__.py +0 -0
  154. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/azure_storage.py +0 -0
  155. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/gcp_storage.py +0 -0
  156. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/local_storage.py +0 -0
  157. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/minio_storage.py +0 -0
  158. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/s3_storage.py +0 -0
  159. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/storage_factory.py +0 -0
  160. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/storage_interface.py +0 -0
  161. {supertable-2.1.1 → supertable-2.2.1}/supertable/storage/tests/test_storage.py +0 -0
  162. {supertable-2.1.1 → supertable-2.2.1}/supertable/super_pipe.py +0 -0
  163. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/__init__.py +0 -0
  164. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_align_to_schema_fix.py +0 -0
  165. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_reader.py +0 -0
  166. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer.py +0 -0
  167. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_data_writer_comprehensive.py +0 -0
  168. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_dedup_on_read_write.py +0 -0
  169. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_newer_than.py +0 -0
  170. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_process_delete_only.py +0 -0
  171. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_processing.py +0 -0
  172. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_query_sql.py +0 -0
  173. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_small_file_compaction.py +0 -0
  174. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_super_table.py +0 -0
  175. {supertable-2.1.1 → supertable-2.2.1}/supertable/tests/test_supertable_all.py +0 -0
  176. {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/__init__.py +0 -0
  177. {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/helper.py +0 -0
  178. {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/sql_parser.py +0 -0
  179. {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/tests/test_sql_parser_columns.py +0 -0
  180. {supertable-2.1.1 → supertable-2.2.1}/supertable/utils/timer.py +0 -0
  181. {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/dependency_links.txt +0 -0
  182. {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/entry_points.txt +0 -0
  183. {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/requires.txt +0 -0
  184. {supertable-2.1.1 → supertable-2.2.1}/supertable.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supertable
3
- Version: 2.1.1
3
+ Version: 2.2.1
4
4
  Summary: SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.
5
5
  Author: Levente Kupas
6
6
  Author-email: Levente Kupas <lkupas@kladnasoft.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "supertable"
7
- version = "2.1.1"
7
+ version = "2.2.1"
8
8
  description = "SuperTable — versioned data lake library for SQL analytics on Parquet + Redis."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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.1.1",
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.1.1"
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
- # RBAC check — also returns per-alias column/row filter definitions
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,