partition_gardener 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dc13f2d2bdae3ecc6cf8e9d66a27f836c1cebbb06bdfd84452e4698e5cb8c63
4
- data.tar.gz: e60370a277cdcd06b9d2376f4d73169ac29792ed9e7f150a6177d1a1e5703c92
3
+ metadata.gz: ca1610832b9a6faff9b6aaf01cbe61b7058df2c7d722911e853e618d8c75c225
4
+ data.tar.gz: 66740e3912d495366737997cb09c9a1b97972c44763b8eb39a2722d2e837e38e
5
5
  SHA512:
6
- metadata.gz: 70d5dabf55e8e6b207768233e2f4f154784531ba35cac9f9731cae76141f81252810acd9bb9fded3c9547c1b60c207be7ad0cf5baca47a950869fa0dd6463ea3
7
- data.tar.gz: 23e10104588d5878c59bb25c3d3116d2cf9ea25793c13dff785a0c90b57f545889ec87bc6b56c5a480a1defdf3b367c0de3ec698ef0b85a1953c9c654c55f64e
6
+ metadata.gz: '068c551bb1e30ba4cbc85407d0fe9204cc2d1e1c3f325fff12aa501be5bcf4038b5af782aed46db0be4ebff458fc2d14956b0879a670da5307814d51c26d1e8e'
7
+ data.tar.gz: 71b0cc6d6695d74dc053265fc69dcef955b952792194ec14e1edb7b177ac1071146b9b7c4db34c4ac13025cac85a3a4655d9f0199b4a043b66894b0c0c804362
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.1 (2026-07-09)
4
+
5
+ - Add partition engines guide mapping portable maintenance patterns to MySQL, Oracle, SQL Server, Db2, YugabyteDB, and Aurora
6
+ - Link the new guide from decision flow, partition landscape, and related tooling docs
7
+
3
8
  ## 0.3.0 (2026-07-08)
4
9
 
5
10
  - Remove `suggest_template`, `recommend`, and register-time layout reliability warnings
data/README.md CHANGED
@@ -19,6 +19,7 @@ Complements migration gems (e.g. [pg_party](https://github.com/rkrage/pg_party))
19
19
 
20
20
  - [docs/decision_flow.md](docs/decision_flow.md) — when to partition, layout, and method choices
21
21
  - [docs/partition_landscape.md](docs/partition_landscape.md) — templates, Rails sharding, pruning, UI, aggregate snapshots, materialized views
22
+ - [docs/partition_engines.md](docs/partition_engines.md) — portable patterns and other database engines
22
23
  - [docs/configuration.md](docs/configuration.md) — global config, registry, per-table options, JSON import
23
24
  - [docs/tooling_split.md](docs/tooling_split.md) — pg_party vs pg_partman vs Gardener
24
25
  - [docs/related_postgres_tooling.md](docs/related_postgres_tooling.md) — PgHero, Dexter, pgsync, pgslice vs Gardener
@@ -19,7 +19,7 @@ root logger 1.7.0 Ruby|BSD-2-Clause
19
19
  root minitest 6.0.6 MIT
20
20
  root parallel 2.1.0 MIT
21
21
  root parser 3.3.11.1 MIT
22
- root partition_gardener 0.3.0 MIT
22
+ root partition_gardener 0.3.1 MIT
23
23
  root pg 1.6.3 BSD-2-Clause
24
24
  root polyrun 1.5.0 MIT
25
25
  root prism 1.9.0 MIT
@@ -0,0 +1,24 @@
1
+ ## Participants
2
+
3
+ Andrei Makarov, agent-assisted release prep.
4
+
5
+ ## Decisions
6
+
7
+ Cut patch release 0.3.1 for documentation shipped after 0.3.0. No runtime or API changes.
8
+
9
+ ## Effects
10
+
11
+ Added product-facing bullets to CHANGELOG.md and bumped lib/partition_gardener/version.rb to 0.3.1.
12
+
13
+ ## Next
14
+
15
+ Run make release on trunk/0.3.1 after merge to main, or release from this branch per maintainer workflow.
16
+
17
+ ## Source
18
+
19
+ Commits since tag 0.3.0:
20
+
21
+ - 67ffcb1 Add documentation for partition engines and related patterns
22
+ - 7d91d4a Add coverage directory creation in GitHub Actions workflow (CI only; omitted from CHANGELOG)
23
+
24
+ New file docs/partition_engines.md maps Gardener's portable patterns (sliding window, premake, default drain, heat splits, keyset rebalance, hot-switch) to other database engines. README and existing guides cross-link the page.
@@ -146,6 +146,7 @@ When in doubt, prefer fewer moving parts: range on a real access key, sliding wi
146
146
 
147
147
  ## Related
148
148
 
149
+ - [partition_engines.md](partition_engines.md) — same patterns on MySQL, Oracle, YSQL, and other engines
149
150
  - [configuration.md](configuration.md) — registry options and `maintenance_backend`
150
151
  - [background_job.md](background_job.md) — schedule `run!` after layout is chosen
151
152
  - [tooling_split.md](tooling_split.md) — pg_party, pg_partman, Gardener ownership
@@ -0,0 +1,475 @@
1
+ # Partition engines and portable patterns
2
+
3
+ Partition Gardener implements runtime maintenance for PostgreSQL native declarative partitioning. The gem is PostgreSQL-specific today: catalog introspection, attach and detach DDL, advisory locks, and pg_partman integration all assume one PostgreSQL connection.
4
+
5
+ The problems Gardener solves are not PostgreSQL-only. Large tables split by time or category need the same operational invariants on every engine that supports partition-shaped lifecycle: bounded catalog size, headroom for future inserts, retention without bulk delete, and a single maintainer that reconciles layout against policy. This page maps those portable patterns to other database engines, states where Gardener's current implementation applies as-is, and where teams should borrow the design without expecting this gem to run unchanged.
6
+
7
+ Use this page with [partition_landscape.md](partition_landscape.md) (templates and PostgreSQL scope), [decision_flow.md](decision_flow.md) (when to partition), and [tooling_split.md](tooling_split.md) (creation vs runtime maintenance).
8
+
9
+ ## Portable patterns Gardener encodes
10
+
11
+ These concepts are engine-agnostic. Gardener's registry, planner, audit, and plan JSON express them for PostgreSQL; another engine would need its own DDL adapter but can reuse the same operator contract.
12
+
13
+ Sliding window with three areas — archive holds finalized buckets before the active window. Current holds a bounded active span with optional heat-driven splits. Future is one open-ended tail. Default (where the engine supports it) is a safety net that must trend empty.
14
+
15
+ Premake and horizon — upper bound of the latest non-default child stays ahead of maximum insert keys. Missing future bounds route inserts to default or fail inserts depending on engine.
16
+
17
+ Default drain last — rows that landed in the catch-all partition move into named children before archive work. Maintenance treats default as mandatory final phase.
18
+
19
+ Heat splits inside current — hot buckets inside the active window get dedicated children; gap fillers cover non-hot buckets so bounds stay contiguous.
20
+
21
+ Keyset rebalance — row moves use composite cursor on partition key plus conflict key, not offset pagination. Idempotent batches delete from source after insert.
22
+
23
+ Retention by whole child — old periods leave via detach or drop of a partition child, not `DELETE` across the parent. Snapshot and rollup jobs must refresh affected buckets after moves ([partition_landscape.md](partition_landscape.md#aggregates-totals-and-snapshots)).
24
+
25
+ Single maintainer per parent — one owner per table per run; overlapping premake cron and layout repair is a configuration error ([tooling_split.md](tooling_split.md)).
26
+
27
+ Plan before apply — dry-run plan, audit warnings, indicator thresholds, and `retention_apply: false` preview mirror operator trust models on any engine.
28
+
29
+ Hot-switch cutover — shadow partitioned parent, minimal premake, atomic rename, then runtime maintenance owns the window ([cutover.md](cutover.md)).
30
+
31
+ Template registry — sliding_window_monthly, list_split, hash_branches, and composite trees are layout policies independent of PostgreSQL syntax.
32
+
33
+ Reliability invariants from [decision_flow.md](decision_flow.md#reliability-invariants) transfer: partition key in unique constraints, no overlapping bounds, one owner, retention by child removal, analyze after large reshapes where statistics drive pruning.
34
+
35
+ ## At a glance by engine
36
+
37
+ PostgreSQL — full Gardener target. Declarative RANGE, LIST, HASH, composite sub-partition trees, default partition, ATTACH/DETACH, CONCURRENTLY detach (14+).
38
+
39
+ YugabyteDB YSQL — high pattern overlap, gem not validated. PostgreSQL-compatible declarative partitioning (RANGE, LIST, HASH), default partition, attach and detach. Distributed tablets and geo-partitioning add placement concerns Gardener does not model. Likely needs connection adapter and staging tests; core planner concepts map.
40
+
41
+ MySQL and MariaDB — partial pattern overlap, no Gardener today. RANGE, LIST, HASH, KEY, and subpartitioning. No PostgreSQL-style default partition; out-of-range inserts error. Premake via `ADD PARTITION` / `REORGANIZE PARTITION`. Retention via `DROP PARTITION` or `EXCHANGE PARTITION` with staging tables. Sliding window and hot-switch map to exchange-based workflows, not attach rebalance.
42
+
43
+ Oracle — partial overlap, no Gardener today. Interval partitioning auto-creates future range partitions on insert (built-in premake). Rolling window still needs scheduled `DROP PARTITION` / `TRUNCATE PARTITION`. Composite LIST-RANGE and interval-hash match Gardener composite templates conceptually. Heat splits and default drain have no direct analogue.
44
+
45
+ SQL Server (MSSQL) — partial overlap, no Gardener today. Partition function plus scheme; sliding window via SWITCH, SPLIT, MERGE, NEXT USED. Hot-switch and retention map closely; no default partition. Azure SQL Database and managed instance use the same verbs. See [SQL Server (MSSQL)](#sql-server-mssql) below.
46
+
47
+ IBM Db2 — high overlap for detach/attach semantics, no Gardener today. RANGE partitioning with `ALTER TABLE ... DETACH PARTITION ... INTO` and `ATTACH PARTITION`; roll-in/roll-out matches PostgreSQL archive story. Async detach task and SET INTEGRITY differ from PostgreSQL.
48
+
49
+ Amazon Aurora MySQL — same as MySQL partitioning limits and EXCHANGE workflow; cluster storage is opaque. Gardener patterns apply at the MySQL layer.
50
+
51
+ CockroachDB — low overlap for Gardener detach/drop model. Partitions are logical subdivisions of one physical table, not separately droppable children. No bulk drop by partition; repartition or row-level TTL for expiry. Range and list partitioning help geo zones and pruning, not Gardener-style archive detach. Borrow audit indicators and application contract; implement retention with TTL or batched DELETE.
52
+
53
+ ClickHouse — low overlap for row rebalance; high overlap for retention shape. `PARTITION BY` on MergeTree; TTL and `ALTER TABLE ... DROP PARTITION` for lifecycle. Premake is implicit as data arrives into new parts. No cross-partition row moves like PostgreSQL default drain. Align partition granularity with TTL ([ClickHouse TTL guidance](https://clickhouse.com/docs/guides/developer/ttl)). Gardener's sliding window is policy documentation; engine TTL does the work.
54
+
55
+ TimescaleDB — out of scope ([partition_landscape.md](partition_landscape.md#timescaledb-hypertables)). Hypertable chunks and compression policies replace declarative children.
56
+
57
+ Citus — out of scope ([partition_landscape.md](partition_landscape.md#citus-distributed-tables)). Distribution across workers is not declarative partitioning on one instance.
58
+
59
+ Snowflake, BigQuery, Redshift — warehouse partitioning and clustering are metadata for pruning and storage layout, not attachable child tables. Borrow horizon, retention, and snapshot patterns; maintenance is DDL or TTL at the warehouse layer, not Gardener.
60
+
61
+ SQLite — no native table partitioning. Application-level shard tables or archive tables only.
62
+
63
+ ## PostgreSQL and YugabyteDB YSQL
64
+
65
+ ### PostgreSQL
66
+
67
+ Gardener's reference engine. Templates in [partition_landscape.md](partition_landscape.md#implemented-templates) map directly to `PARTITION BY` methods. `Executor` issues `CREATE TABLE ... PARTITION OF`, `ATTACH PARTITION`, `DETACH PARTITION`, and keyset moves between children.
68
+
69
+ Extension and tool boundaries stay as documented: pg_partman for plain premake ([tooling_split.md](tooling_split.md)), Timescale and Citus out of scope, ankane tools for adjacent layers ([related_postgres_tooling.md](related_postgres_tooling.md)).
70
+
71
+ ### YugabyteDB YSQL
72
+
73
+ YSQL implements PostgreSQL-style declarative partitioning: `PARTITION BY RANGE | LIST | HASH`, `CREATE TABLE ... PARTITION OF`, default partition, attach, and detach ([YugabyteDB table partitioning](https://docs.yugabyte.com/stable/explore/ysql-language-features/advanced-features/partitions/), [partition by time](https://docs.yugabyte.com/stable/develop/data-modeling/common-patterns/timeseries/partitioning-by-time/)).
74
+
75
+ Patterns that transfer from Gardener without semantic change:
76
+
77
+ - Monthly or daily range windows with default catch-all
78
+ - Retention by `DROP TABLE` on detached or attached partition children
79
+ - Composite primary key including partition key for uniqueness and pruning
80
+ - List-then-range composite trees for geo or branch plus time
81
+ - Application contract: filters on partition key, UI scoped to period or tenant ([partition_landscape.md](partition_landscape.md#rails-application-contract))
82
+
83
+ Gardener-specific gaps on YugabyteDB:
84
+
85
+ - No shipped `YugabyteConnection` or CI matrix against YSQL
86
+ - Catalog queries target `pg_catalog` shapes; verify `pg_inherits` and `pg_get_expr` bound parsing on target versions
87
+ - Each child is also sharded into tablets; detach/drop affects DocDB placement — plan replication and zone policies outside Gardener
88
+ - Foreign keys referencing partitioned parents remain constrained ([YugabyteDB FK discussion](https://dev.to/yugabyte/foreign-keys-referencing-partitioned-tables-in-yugabytedb-26pn)); same composite-key rule as PostgreSQL
89
+
90
+ Practical path: treat Gardener as a design reference and operator checklist first. Pilot on YSQL with `connection_resolver` pointed at a cluster and compare `plan` output to manual `information_schema` / catalog inspection before any production `run!`.
91
+
92
+ ## MySQL and MariaDB
93
+
94
+ MySQL partitioning is declared at `CREATE TABLE` with RANGE, LIST, HASH, or KEY; subpartitioning adds a second level ([MySQL partitioning types](https://dev.mysql.com/doc/refman/8.4/en/partitioning-types.html)).
95
+
96
+ ### What matches Gardener patterns
97
+
98
+ Range by date or timestamp — maps to `sliding_window_monthly`, `sliding_window_daily`, and calendar year templates. Retention via `ALTER TABLE ... DROP PARTITION` for old months.
99
+
100
+ List by stable category — maps to `list_split` and LIST parent in composite templates.
101
+
102
+ Hash modulus — maps to `hash_branches`; changing modulus is a major migration on every engine.
103
+
104
+ Composite RANGE + HASH or RANGE + KEY — maps to `composite_list_range` and `composite_range_hash` conceptually via subpartitioning.
105
+
106
+ Hot-switch — `ALTER TABLE ... EXCHANGE PARTITION ... WITH TABLE` is metadata-heavy row swap, analogous to pgslice swap and Gardener `hot_switch_tables` ([related_postgres_tooling.md](related_postgres_tooling.md#pgslice)).
107
+
108
+ ### What differs
109
+
110
+ No default partition — inserts outside defined bounds fail with error 1526 rather than routing to `_default`. Gardener's default-drain-last phase has no target; premake and horizon are stricter operational requirements.
111
+
112
+ Child identity — partitions are numbered (`p0`, `p1`, ...) or named in DDL; not separate freely named tables until exchange workflows. Gardener's `{table}_current` / `{table}_future` naming ([naming.md](naming.md)) would be custom DDL, not native attach slots.
113
+
114
+ Layout repair — adding a range uses `ADD PARTITION` or `REORGANIZE PARTITION`, not PostgreSQL `ATTACH` of a pre-filled child. Row moves for backfill often use `EXCHANGE PARTITION` with a staging table rather than keyset `INSERT ... DELETE` across attached children.
115
+
116
+ Unique constraints — partition key must be part of every unique index ([MySQL constraints](https://dev.mysql.com/doc/refman/8.4/en/partitioning-limitations.html)); same rule as PostgreSQL.
117
+
118
+ ### How Gardener helps without running on MySQL
119
+
120
+ Use Gardener's JSON plan and audit shape as a specification for a MySQL maintainer: same registry fields (`partition_key_column`, `active_months`, `retention_months`, `split_row_threshold`), same indicators (horizon, heat, gap warnings), different executor issuing `ADD PARTITION`, `DROP PARTITION`, and `EXCHANGE`.
121
+
122
+ Recommended MySQL sliding window (conceptual):
123
+
124
+ 1. Premake next month with `ADD PARTITION` before the first day of the new month.
125
+ 2. Keep partition count bounded; `DROP PARTITION` for months older than retention.
126
+ 3. For cutover from non-partitioned table, load staging table and `EXCHANGE PARTITION` per month ([exchange partitions](https://dev.mysql.com/doc/refman/8.4/en/partitioning-management-exchange.html)).
127
+ 4. Enforce application filters on partition key; no default safety net.
128
+
129
+ 4. Enforce application filters on partition key; no default safety net.
130
+
131
+ ## IBM Db2
132
+
133
+ Db2 table partitioning splits rows across data partitions by RANGE on one or more columns ([table partitioning](https://www.ibm.com/docs/en/db2/11.1.0?topic=tables-table-partitioning)). Roll-in and roll-out use `ATTACH PARTITION` and `DETACH PARTITION` on `ALTER TABLE` ([detaching partitions](https://www.ibm.com/docs/en/db2/11.5.x?topic=ranges-detaching-data-partitions), [rolling scenarios](https://www.ibm.com/docs/en/db2/11.1.0?topic=apt-scenarios-rolling-in-rolling-out-partitioned-table-data)).
134
+
135
+ ### What matches Gardener patterns
136
+
137
+ Detach for retention — `ALTER TABLE stock DETACH PARTITION dec01 INTO stock_drop` turns a range into a standalone table without row movement; drop or archive the standalone table. Same intent as PostgreSQL `DETACH` then `DROP` ([retention.md](retention.md)).
138
+
139
+ Attach for premake — roll new ranges in from standalone tables or empty attached partitions waiting for `SET INTEGRITY`.
140
+
141
+ Sliding window — detach oldest month, attach new month; bounded partition count matches Gardener archive plus future discipline.
142
+
143
+ No overlapping ranges — attach boundaries must not overlap existing ranges; gap detection and horizon warnings from Gardener audit translate directly.
144
+
145
+ ### What differs
146
+
147
+ SET INTEGRITY — attached partitions may require `SET INTEGRITY` before fully visible; Gardener has no equivalent phase.
148
+
149
+ Async detach — detach completes in phases; queries under some isolation levels continue during detach.
150
+
151
+ Distribution — `DISTRIBUTE BY HASH` plus `PARTITION BY RANGE` combines Db2 DPF with table partitioning; Gardener does not model database partition groups.
152
+
153
+ No PostgreSQL default partition name — overflow behavior is attach discipline and range design, not `_default` drain.
154
+
155
+ ### How Gardener helps on Db2
156
+
157
+ Db2 is the strongest non-PostgreSQL candidate after YugabyteDB YSQL for a detach/attach executor port. Registry, planner layout, retention months, and plan JSON map with DDL swapped to Db2 `ATTACH`/`DETACH`. Validate `SET INTEGRITY` and async detach in integration specs before production `apply`.
158
+
159
+ ## Oracle
160
+
161
+ Oracle offers range, list, hash, reference, interval, and composite partitioning ([partitioning concepts](https://docs.oracle.com/en/database/oracle/oracle-database/26/vldbg/partition-concepts.html)).
162
+
163
+ ### What matches Gardener patterns
164
+
165
+ Interval partitioning — automatic creation of range partitions when data exceeds the transition point. This is built-in premake; Gardener `premake_monthly` and future-zone repair are partly redundant for forward bounds.
166
+
167
+ Range and composite — monthly archive plus list branch maps to Gardener templates at the policy level.
168
+
169
+ Retention — `ALTER TABLE ... DROP PARTITION` / `TRUNCATE PARTITION` for closed intervals; aligns with [retention.md](retention.md) detach-then-drop semantics when partitions are materialized.
170
+
171
+ Rolling window — still requires scheduled drop of old intervals; interval partitioning does not remove the need for retention automation ([Oracle maintenance](https://docs.oracle.com/en/database/oracle/oracle-database/21/vldbg/maintenance-partition-tables-indexes.html)).
172
+
173
+ ### What differs
174
+
175
+ No PostgreSQL default partition — routing semantics differ; "overflow" behavior is interval extension, not a `_default` child.
176
+
177
+ Heat splits inside current — Oracle subpartition add on materialized intervals is possible but not the same as Gardener heatmap-driven dedicated months inside a sliding current zone.
178
+
179
+ Global indexes — partition maintenance can mark global index partitions unusable unless `UPDATE INDEXES` is used; Gardener's per-child local index story does not cover Oracle global index rebuild policy.
180
+
181
+ ### How Gardener helps on Oracle
182
+
183
+ Borrow the decision flow and indicator set: when interval partitioning is enough, run drop-only retention jobs; when composite LIST-RANGE or manual range is used, document three-area layout in registry JSON even if Oracle DDL is hand-written PL/SQL.
184
+
185
+ Teams often combine interval premake with a scheduled procedure to drop partitions older than N months — Gardener's `retention_months` and audit warnings are the checklist for that job.
186
+
187
+ ## SQL Server (MSSQL)
188
+
189
+ SQL Server, Azure SQL Database, and Azure SQL Managed Instance share the same partition model: a partition function defines boundary values, a partition scheme maps partition numbers to filegroups, and the table references both at create time ([partitioned tables and indexes](https://learn.microsoft.com/en-us/sql/relational-databases/partitions/partitioned-tables-and-indexes?view=sql-server-ver17)).
190
+
191
+ Partition Gardener does not run on SQL Server today. The overlap with Gardener is among the strongest of any non-PostgreSQL engine for sliding-window retention and hot-switch cutover, because `SWITCH` is metadata-only like PostgreSQL attach/detach and pgslice swap.
192
+
193
+ ### Objects and catalog
194
+
195
+ Partition function — maps partition key values to partition numbers (`CREATE PARTITION FUNCTION ... AS RANGE LEFT | RIGHT FOR VALUES (...)`).
196
+
197
+ Partition scheme — maps partition numbers to filegroups (`CREATE PARTITION SCHEME ... AS PARTITION function_name TO (...)`).
198
+
199
+ Partitioned heap or clustered index — `CREATE TABLE ... ON scheme(column)`; nonclustered indexes inherit the table scheme unless overridden.
200
+
201
+ Staging tables — for sliding window and cutover, aligned nonpartitioned tables (same columns, indexes, constraints) receive switched-out data.
202
+
203
+ Audit queries — `sys.partition_functions`, `sys.partition_schemes`, `sys.partition_range_values`, `sys.partitions`, and `$PARTITION.function_name(column)` for row routing checks ([$PARTITION](https://learn.microsoft.com/en-us/sql/t-sql/functions/partition-transact-sql?view=sql-server-ver17)).
204
+
205
+ RANGE LEFT vs RIGHT — controls which side of a boundary value belongs to which partition. For datetime keys, wrong choice splits a calendar day across partitions. Pick once at function creation and mirror it in maintenance scripts ([sliding window notes](https://weblogs.sqlteam.com/dang/2008/08/30/sliding-window-table-partitioning/)).
206
+
207
+ ### Gardener phase mapping
208
+
209
+ Gardener sliding_window_monthly intent maps to SQL Server operations as follows.
210
+
211
+ Premake / future zone — read max boundary from `sys.partition_range_values`; while horizon is short, run `ALTER PARTITION SCHEME ... NEXT USED filegroup` then `ALTER PARTITION FUNCTION ... SPLIT RANGE (new_boundary)`. SPLIT on an empty trailing partition is fast (metadata only). This is Gardener `horizon_days` and future-tail repair.
212
+
213
+ Archive / retention — `ALTER TABLE Orders SWITCH PARTITION partition_number TO Orders_Archive` (aligned staging), then `TRUNCATE TABLE Orders_Archive` or export and drop staging, then `ALTER PARTITION FUNCTION ... MERGE RANGE (old_boundary)` on the vacated low boundary. MERGE requires the merged partition empty. Matches [retention.md](retention.md) detach-then-drop without PostgreSQL child table names.
214
+
215
+ Hot-switch cutover — load shadow partitioned table or staging tables per month, then `SWITCH` partitions into the live partitioned table, or switch whole aligned tables during migration. Closest analogue to Gardener `HotSwitchConcern` and pgslice swap ([cutover.md](cutover.md), [related_postgres_tooling.md](related_postgres_tooling.md#pgslice)).
216
+
217
+ Default drain — no PostgreSQL-style DEFAULT partition. Out-of-range inserts fail unless the function includes a catch-all boundary. Premake discipline is mandatory; there is no `_default` safety net to drain last.
218
+
219
+ Heat splits inside current — not native. SQL Server does not add mid-window monthly children inside one partition without SPLIT that affects function metadata. High-volume months are usually separate filegroups, compression, or archive switching, not Gardener heatmap splits.
220
+
221
+ Keyset rebalance — not attach-style row moves. Data moves via `SWITCH` to aligned tables or insert-select in staging, then switch back.
222
+
223
+ ### SWITCH requirements
224
+
225
+ `ALTER TABLE ... SWITCH PARTITION` succeeds only when source and target are aligned:
226
+
227
+ - Same column count, order, types, and nullability
228
+ - Matching clustered and nonclustered indexes on the same columns
229
+ - Same check constraints and indexed views where applicable
230
+ - Target empty for switch-in, or switch-out rules satisfied
231
+
232
+ Misaligned staging is the main cutover failure mode. Gardener's hot-switch checklist (analyze, delta sync, count compare) applies verbatim; only DDL differs.
233
+
234
+ ### Sliding window maintenance loop (monthly)
235
+
236
+ Typical automated loop after each month closes (RIGHT-range example):
237
+
238
+ 1. Ensure trailing empty partition exists (SPLIT last range if needed).
239
+ 2. SWITCH out the oldest populated partition to `Orders_Stage_Archive`.
240
+ 3. Archive or truncate staging; drop exported data per policy.
241
+ 4. MERGE the vacated low boundary (both sides must be empty per your LEFT/RIGHT strategy).
242
+ 5. SPLIT to extend the high end for future months (NEXT USED + SPLIT).
243
+ 6. Update statistics on affected partitions.
244
+
245
+ Reuse Gardener registry fields as procedure parameters: `retention_months` drives which boundary MERGEs, `active_months` bounds how many populated partitions stay attached, horizon maps to how far ahead SPLIT runs.
246
+
247
+ Microsoft documents automatic sliding-window patterns in [partition strategies](https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008/cc719702(v=sql.100)) and community write-ups ([mssqltips sliding window](https://www.mssqltips.com/sqlservertip/5296/implementation-of-sliding-window-partitioning-in-sql-server-to-purge-data/)).
248
+
249
+ ### Azure SQL Database and managed instance
250
+
251
+ Same T-SQL partition function, scheme, SWITCH, SPLIT, and MERGE on Azure SQL Database and Azure SQL Managed Instance. Filegroup mapping still exists; elastic pools and hyperscale change IO and size limits, not the maintenance verbs.
252
+
253
+ Differences from on-prem:
254
+
255
+ - Partition count and table size limits follow service tier docs; plan partition count below tier caps (historically 15,000 partitions per table on enterprise-class limits; verify current docs for your SKU).
256
+ - No local filegroup tuning on some offerings; scheme may map many partitions to `PRIMARY` or tier-specific storage.
257
+ - Long-running SPLIT/MERGE still benefits from empty partitions; schedule during low traffic like Gardener indicator-driven maintenance.
258
+
259
+ Borrow Gardener audit indicators: max boundary vs today plus horizon, oldest boundary vs retention cutoff, row counts per `$PARTITION` before MERGE.
260
+
261
+ ### Rails and Active Record
262
+
263
+ Rails apps on SQL Server use the SQL Server adapter; models query the parent table name. Partition pruning depends on `WHERE` on the partition key in scopes, same contract as PostgreSQL ([partition_landscape.md](partition_landscape.md#rails-application-contract)).
264
+
265
+ Composite primary keys including the partition key apply when uniqueness is enforced per partition. `query_constraints` (Rails 7.1+) helps targeted updates when the logical key spans partition key plus id.
266
+
267
+ Gardener railtie and CLI do not apply. Run partition maintenance via SQL Agent job or application scheduler calling a stored procedure; keep one maintainer per table.
268
+
269
+ ### What Gardener provides without a port
270
+
271
+ - Registry JSON as policy document (`partition_key_column`, `retention_months`, `active_months`, `bucket: :month`)
272
+ - Plan vocabulary: archive, premake, retention, horizon
273
+ - Audit warning catalog adapted to `$PARTITION` row counts and boundary gaps
274
+ - Cutover playbook structure from [cutover.md](cutover.md)
275
+ - UI and snapshot discipline from [partition_landscape.md](partition_landscape.md)
276
+
277
+ A future `SqlServerConnection` adapter would read `sys.partition_*` instead of `pg_inherits`, emit SPLIT/MERGE/SWITCH instead of ATTACH/DETACH, and use `sp_getapplock` instead of PostgreSQL advisory locks.
278
+
279
+
280
+ ## CockroachDB
281
+
282
+ CockroachDB partitions are logical: one physical table subdivided for zone configs and pruning, not separately droppable tables ([table partitioning](https://www.cockroachlabs.com/docs/stable/partitioning)).
283
+
284
+ ### What matches Gardener patterns
285
+
286
+ Range by date for archival placement — move cold ranges to cheaper nodes via zone configs; same product goal as archive zone in sliding window.
287
+
288
+ List for geo — maps to `list_split` policy for region or tenant keys.
289
+
290
+ Application contract — filters on partition key for pruning; UI scoped to region or period ([partition_landscape.md](partition_landscape.md#ui-and-product-surfaces)).
291
+
292
+ Audit indicators — default row count is not applicable; horizon becomes "repartition needed before insert range exceeds defined bounds".
293
+
294
+ ### What differs
295
+
296
+ No `DETACH PARTITION` / `DROP PARTITION` for bulk removal — drop data with row-level TTL, changefeed plus batched `DELETE`, or repartition the table ([archival partitioning blog](https://blog.cloudneutral.se/archival-partitioning-with-cockroachdb), [row-level TTL](https://www.cockroachlabs.com/docs/stable/row-level-ttl)).
297
+
298
+ Repartition instead of attach — changing bounds is `ALTER TABLE ... PARTITION BY` or `PARTITION BY NOTHING`, not Gardener tail rebalance.
299
+
300
+ Default drain — no default child; inserts outside defined ranges error or require repartition.
301
+
302
+ ### How Gardener helps on CockroachDB
303
+
304
+ Gardener is a poor fit as a runtime gem. Use decision_flow and partition_landscape for when to partition, snapshot discipline, and UI contract. Implement retention with TTL jobs and monitor `sql.ttl` job metrics, not Gardener `run!`.
305
+
306
+ ## ClickHouse
307
+
308
+ ClickHouse uses `PARTITION BY` expression on MergeTree family engines; partitions are sets of parts on disk ([partitions](https://clickhouse.com/docs/partitions), [delete old data](https://clickhouse.com/docs/faq/operations/delete-old-data)).
309
+
310
+ ### What matches Gardener patterns
311
+
312
+ Monthly or daily partition key — `toYYYYMM(date)` or `toStartOfMonth(date)` matches `sliding_window_monthly` and daily templates.
313
+
314
+ Retention — `ALTER TABLE ... DROP PARTITION` or TTL when partition key aligns with TTL expression; whole-part drop is the analog of Gardener archive detach/drop.
315
+
316
+ Horizon — less critical; new partitions appear as parts when data arrives. Policy focus shifts to TTL and `ttl_only_drop_parts` rather than premake DDL.
317
+
318
+ ### What differs
319
+
320
+ No row moves between partitions — mutations rewrite parts; no default partition drain or keyset rebalance between children.
321
+
322
+ Heat splits — not applicable inside one month; scale is vertical merging and part sizing, not extra child tables.
323
+
324
+ OLTP mixed workload — ClickHouse is analytics-first; Gardener's three-area OLTP layout targets PostgreSQL-style insert routing.
325
+
326
+ ### How Gardener helps on ClickHouse
327
+
328
+ Use Gardener registry fields as documentation: `retention_months` becomes TTL interval; `bucket: :month` becomes `PARTITION BY toYYYYMM(ts)`. Align TTL granularity with partition expression per ClickHouse guidance. Gardener audit concepts (stale snapshots, unbounded UI queries) still apply ([partition_landscape.md](partition_landscape.md#aggregates-totals-and-snapshots)).
329
+
330
+ ## Warehouses and analytics engines
331
+
332
+ ### Snowflake, BigQuery, Redshift
333
+
334
+ These systems expose partition columns or clustering keys for pruning and storage billing, not PostgreSQL-style child tables.
335
+
336
+ Portable from Gardener:
337
+
338
+ - Partition key choice from query patterns ([decision_flow.md](decision_flow.md))
339
+ - Retention policy expressed as months or days of data
340
+ - Snapshot tables for dashboard totals instead of scanning all history
341
+ - UI defaults to bounded periods
342
+
343
+ Not portable:
344
+
345
+ - `plan` / `attach` / `default drain` / advisory lock maintenance loop
346
+ - Template registry tied to physical child names
347
+
348
+ Implement lifecycle in warehouse-native DDL (partition filter on load, table expiration, lifecycle rules) using Gardener docs as the policy spec.
349
+
350
+ Snowflake — micro-partitions are automatic; `CLUSTER BY` aids pruning. Retention via time travel limits and table-level drop or truncate, not monthly child detach.
351
+
352
+ BigQuery — ingestion-time or column partitioning on load; partition expiration policies replace Gardener `retention_months`.
353
+
354
+ Redshift — distribution and sort keys; slice-level layout unlike declarative children. Archive with unload to object storage plus delete, or query via external tables.
355
+
356
+ ### Greenplum and other MPP Postgres forks
357
+
358
+ Greenplum distributes across segments; declarative partitioning syntax may exist per version but maintenance is entangled with segment distribution. Treat like Citus: Gardener does not own segment placement. Product-specific validation required before any port.
359
+
360
+ SingleStore — distributed SQL with shard keys plus optional `PARTITION BY`; combine MySQL-style exchange patterns with cluster routing. Gardener registry documents policy; executor would be product-specific.
361
+
362
+ ## Mapping Gardener templates to other engines
363
+
364
+ sliding_window_monthly — PostgreSQL and YSQL: full. Db2: DETACH/ATTACH monthly RANGE. MySQL: ADD/DROP monthly RANGE. Oracle: interval or range plus drop job. SQL Server: SWITCH/SPLIT/MERGE monthly function. ClickHouse: `PARTITION BY toYYYYMM`. CockroachDB: range partitions plus TTL for drop. Warehouse: partition column or ingestion time.
365
+
366
+ sliding_window_daily — Same engines with day granularity; ClickHouse `toYYYYMMDD`.
367
+
368
+ calendar_year — Yearly RANGE or list of years; Oracle interval with year expression; warehouses use year column.
369
+
370
+ integer_window — PostgreSQL, YSQL, MySQL HASH/RANGE on id bands; SQL Server partition function on id ranges.
371
+
372
+ list_split — PostgreSQL, YSQL, MySQL LIST, Oracle LIST, CockroachDB list for geo.
373
+
374
+ hash_branches — PostgreSQL HASH, MySQL KEY/HASH, Oracle hash subpartitions; fixed modulus at DDL everywhere.
375
+
376
+ composite_list_range / composite_list_hash — PostgreSQL subpartition, MySQL subpartitioning, Oracle composite; not one-shot in CockroachDB without repartition planning.
377
+
378
+ premake_monthly — PostgreSQL Gardener bridge; Oracle interval supersedes; MySQL and SQL Server need explicit premake scripts; ClickHouse N/A.
379
+
380
+ rolling_current_monthly — Experimental everywhere; wide single current child risks catalog and pruning tradeoffs Gardener documents for PostgreSQL.
381
+
382
+ ## What a multi-engine future would require
383
+
384
+ Partition Gardener today splits cleanly into:
385
+
386
+ Portable core — registry, templates, planner layout math (`DateBucket`, `SlidingWindow`, heat maps), plan diff, audit warning catalog, run records, CLI plan/audit.
387
+
388
+ Engine adapter — catalog introspection, bound parsing, DDL for create/attach/detach/exchange/split, row move strategy, lock primitive, extension probes (pg_partman equivalent).
389
+
390
+ A second engine adapter would implement the same `Connection` contract `Executor` expects: quoted identifiers, partition listing, row counts per child, and mutating DDL. PostgreSQL-specific pieces live in `PgConnection`, `Connection` catalog SQL, and `MaintenanceBackend` partman probe.
391
+
392
+ Minimal expansion order by pattern overlap:
393
+
394
+ 1. YugabyteDB YSQL — validate catalog compatibility and run integration specs against a cluster.
395
+ 2. IBM Db2 — detach/attach executor; SET INTEGRITY phase after attach.
396
+ 3. SQL Server — switch/split/merge executor; `sys.partition_*` metadata instead of `pg_inherits`.
397
+ 4. MySQL — exchange-based executor; no default drain phase.
398
+
399
+ CockroachDB, ClickHouse, and warehouses need different products (TTL-first or warehouse lifecycle), not a port of attach/detach rebalance.
400
+
401
+ ## Operator checklist when the engine is not PostgreSQL
402
+
403
+ Keep from Gardener regardless of engine:
404
+
405
+ - Partition key in all unique constraints and hot-path queries
406
+ - One maintainer per table; no overlapping premake crons
407
+ - Retention by removing whole partitions or TTL, not unbounded DELETE
408
+ - Plan or dry-run before destructive DDL
409
+ - Snapshot totals for cross-period aggregates
410
+ - UI and APIs default to partition-scoped windows ([partition_landscape.md](partition_landscape.md#ui-and-product-surfaces))
411
+ - After large data movement, refresh statistics and dependent rollups
412
+
413
+ Replace Gardener-specific steps:
414
+
415
+ - Default drain last — only when the engine has a catch-all partition
416
+ - `DETACH CONCURRENTLY` — PostgreSQL 14+ only
417
+ - Advisory lock — engine-specific distributed lock or job mutex
418
+ - `maintenance_backend: :pg_partman` — PostgreSQL extension only
419
+
420
+ ## Related in this repository
421
+
422
+ - [partition_landscape.md](partition_landscape.md) — PostgreSQL templates, pruning, out of scope extensions
423
+ - [decision_flow.md](decision_flow.md) — whether and how to partition
424
+ - [tooling_split.md](tooling_split.md) — creation vs runtime maintenance on PostgreSQL
425
+ - [related_postgres_tooling.md](related_postgres_tooling.md) — adjacent Postgres tools
426
+ - [retention.md](retention.md) — detach and drop on PostgreSQL
427
+ - [naming.md](naming.md) — child naming catalog
428
+ - [cutover.md](cutover.md) — hot-switch playbook
429
+
430
+ ## Source
431
+
432
+ PostgreSQL and portable concepts:
433
+
434
+ - [PostgreSQL: Table partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html)
435
+ - [Tiny story PostgreSQL partitioning and Rails](https://amkisko.github.io/posts/20260306112539_tiny_story_postgresql_partitioning_and_rails.html)
436
+
437
+ YugabyteDB:
438
+
439
+ - [Table partitioning](https://docs.yugabyte.com/stable/explore/ysql-language-features/advanced-features/partitions/)
440
+ - [Partition data by time](https://docs.yugabyte.com/stable/develop/data-modeling/common-patterns/timeseries/partitioning-by-time/)
441
+ - [YSQL row-level partitioning design](https://github.com/yugabyte/yugabyte-db/blob/master/architecture/design/ysql-row-level-partitioning.md)
442
+
443
+ MySQL:
444
+
445
+ - [Partitioning types](https://dev.mysql.com/doc/refman/8.4/en/partitioning-types.html)
446
+ - [Exchange partitions](https://dev.mysql.com/doc/refman/8.4/en/partitioning-management-exchange.html)
447
+
448
+ Oracle:
449
+
450
+ - [Partitioning concepts](https://docs.oracle.com/en/database/oracle/oracle-database/26/vldbg/partition-concepts.html)
451
+ - [Maintenance operations](https://docs.oracle.com/en/database/oracle/oracle-database/21/vldbg/maintenance-partition-tables-indexes.html)
452
+
453
+ SQL Server:
454
+
455
+ - [Partitioned tables and indexes](https://learn.microsoft.com/en-us/sql/relational-databases/partitions/partitioned-tables-and-indexes?view=sql-server-ver17)
456
+ - [ALTER PARTITION FUNCTION](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-partition-function-transact-sql?view=sql-server-ver17)
457
+ - [$PARTITION](https://learn.microsoft.com/en-us/sql/t-sql/functions/partition-transact-sql?view=sql-server-ver17)
458
+ - [Sliding window partitioning](https://www.mssqltips.com/sqlservertip/5296/implementation-of-sliding-window-partitioning-in-sql-server-to-purge-data/)
459
+
460
+ IBM Db2:
461
+
462
+ - [Table partitioning](https://www.ibm.com/docs/en/db2/11.1.0?topic=tables-table-partitioning)
463
+ - [Detaching data partitions](https://www.ibm.com/docs/en/db2/11.5.x?topic=ranges-detaching-data-partitions)
464
+ - [Rolling in and rolling out](https://www.ibm.com/docs/en/db2/11.1.0?topic=apt-scenarios-rolling-in-rolling-out-partitioned-table-data)
465
+
466
+ CockroachDB:
467
+
468
+ - [Table partitioning](https://www.cockroachlabs.com/docs/stable/partitioning)
469
+ - [Row-level TTL](https://www.cockroachlabs.com/docs/stable/row-level-ttl)
470
+
471
+ ClickHouse:
472
+
473
+ - [Table partitions](https://clickhouse.com/docs/partitions)
474
+ - [TTL guide](https://clickhouse.com/docs/guides/developer/ttl)
475
+ - [Delete old data FAQ](https://clickhouse.com/docs/faq/operations/delete-old-data)
@@ -281,6 +281,10 @@ Hypertables use chunk policies and compression, not declarative RANGE children.
281
281
 
282
282
  Citus distributes tables across worker nodes inside PostgreSQL with its own shard metadata and rebalance tooling. That is neither Rails `connects_to` sharding nor native declarative partitioning on a single instance. Gardener does not drive Citus shard placement.
283
283
 
284
+ ### Other database engines
285
+
286
+ Partition Gardener targets PostgreSQL declarative partitioning. The maintenance patterns (sliding window, retention, premake, default drain, hot-switch) apply on other engines with different DDL. YugabyteDB YSQL is the closest PostgreSQL-compatible fit; MySQL, Oracle, and SQL Server share partial overlap; CockroachDB and ClickHouse need TTL-first or repartition models. See [partition_engines.md](partition_engines.md) for the full engine map and what transfers without running this gem.
287
+
284
288
  ### UUIDv7 and encoded-time keys
285
289
 
286
290
  Time-ordered UUID primary keys are increasingly common. Partitioning on `date_trunc` of a timestamp column remains the recommended Gardener path. For UUID-native range strategies, use pg_partman 5.2+ or custom DDL; Gardener does not parse UUID time components today.
@@ -192,4 +192,4 @@ Cross-link [Dexter](https://github.com/ankane/dexter) in [operations.md](operati
192
192
  - [Dexter](https://github.com/ankane/dexter)
193
193
  - [pgsync](https://github.com/ankane/pgsync)
194
194
  - [pgslice](https://github.com/ankane/pgslice)
195
- - In-repo: [tooling_split.md](tooling_split.md), [cutover.md](cutover.md), [monitoring.md](monitoring.md), [partition_landscape.md](partition_landscape.md)
195
+ - In-repo: [tooling_split.md](tooling_split.md), [cutover.md](cutover.md), [monitoring.md](monitoring.md), [partition_landscape.md](partition_landscape.md), [partition_engines.md](partition_engines.md)
@@ -1,3 +1,3 @@
1
1
  module PartitionGardener
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: partition_gardener
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Makarov
@@ -265,6 +265,7 @@ files:
265
265
  - docs/application_contract.md
266
266
  - docs/audit_reference.md
267
267
  - docs/background_job.md
268
+ - docs/changelogs/20260709120900_partition-engines-guide.md
268
269
  - docs/cli.md
269
270
  - docs/configuration.md
270
271
  - docs/cutover.md
@@ -273,6 +274,7 @@ files:
273
274
  - docs/monitoring.md
274
275
  - docs/naming.md
275
276
  - docs/operations.md
277
+ - docs/partition_engines.md
276
278
  - docs/partition_landscape.md
277
279
  - docs/pg_party_recipe.md
278
280
  - docs/related_postgres_tooling.md