API Reference¶
Auto-generated from source using mkdocstrings.
pg_partsmith¶
Top-level public API: entities, enums, exceptions, and period calculators.
Entities¶
Represents a time period for partition boundaries.
Every period has exactly one granularity kind (a
:class:PartitionGranularity member), decided once by
_granularity_key. All kind-specific behaviour (validation,
arithmetic, ordering, formatting) lives in the _SPECS table below,
so supporting a new kind means adding one _GranularitySpec entry.
Attributes:
| Name | Type | Description |
|---|---|---|
year |
int
|
Year component. |
month |
int | None
|
Month component (1-12), optional. |
day |
int | None
|
Day component (1-31), optional. |
week |
int | None
|
ISO week number (1-53), optional. |
hour |
int | None
|
Hour component (0-23), optional; requires day. |
quarter |
int | None
|
Quarter component (1-4), optional. |
Source code in pg_partsmith/entities.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
__add__(offset)
¶
__lt__(other)
¶
Compare periods of the same granularity kind.
Source code in pg_partsmith/entities.py
__post_init__()
¶
__str__()
¶
__sub__(offset)
¶
to_date()
¶
Return the period's start date.
Weekly periods return the Monday of the ISO week; hourly periods
return the calendar date (use :meth:to_datetime to preserve the
hour component).
Source code in pg_partsmith/entities.py
to_datetime()
¶
Convert period start to a timezone-aware UTC datetime.
Unlike :meth:to_date, preserves the hour component, so hourly
periods within one day map to distinct instants.
Source code in pg_partsmith/entities.py
Bases: BaseModel
Metadata about a partition.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
StrippedNonEmptyStr
|
Partition table name. |
partition_type |
PartitionType
|
Type of partition (RANGE, LIST, HASH). |
from_value |
str | None
|
Start boundary value (for RANGE). |
to_value |
str | None
|
End boundary value (for RANGE). |
boundaries_expr |
str | None
|
Raw boundary expression as reported by PostgreSQL
( |
bounds |
PartitionBounds | None
|
Structured form of the same boundaries, discriminated on the
bound kind. Populated from |
is_attached |
bool
|
Whether partition is currently attached to parent table. |
is_default |
bool
|
Whether this is the DEFAULT partition (no explicit boundaries). |
subpartition_type |
PartitionType | None
|
How this partition partitions its own children, when
it is itself a partitioned table. |
parent_table |
StrippedNonEmptyStr | None
|
Name of parent partitioned table. |
Source code in pg_partsmith/entities.py
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | |
hash_bounds
property
¶
This partition's MODULUS/REMAINDER bounds, when hash-bound.
is_subpartitioned
property
¶
True when this partition is itself a partitioned table (a branch).
relname
property
¶
Bare relation name without the schema qualifier.
list_partitions always returns schema-qualified names; use this
when addressing the partition through code that works with bare names
(period parsing, export layouts, catalogue lookups).
schema_name
property
¶
Schema part of :attr:name, or None when the name is unqualified.
derive_range_bounds(data)
classmethod
¶
Keep bounds and from_value/to_value in step.
Both spellings of a RANGE boundary are part of the public surface:
callers written before structured bounds existed pass the pair, newer
ones pass bounds. Deriving the missing side here means neither kind
of caller can observe a half-populated model.
Source code in pg_partsmith/entities.py
validate_range_boundaries()
¶
Validate that attached RANGE partitions have boundaries.
Detached (orphaned) partitions may have lost their boundary metadata
from the catalog and are allowed to carry None boundaries.
For attached partitions we accept either parsed boundaries
(from_value + to_value) OR a raw boundaries expression so that
callers can still reason about partitions even when expression parsing
fails.
Source code in pg_partsmith/entities.py
Bases: BaseModel
Configuration for table partitioning maintenance.
A root is either time-based — RANGE over a date/time dimension, with a
create-ahead window and a retention window — or static: HASH_BASED or
VALUE_BASED, divided into a fixed set of partitions described by
:attr:root_layout, which neither grows with the clock nor ages out.
Either kind can be subpartitioned further.
Attributes:
| Name | Type | Description |
|---|---|---|
schema |
Optional schema name for the partitioned table. When set, all DDL and catalogue queries are schema-qualified, making behaviour deterministic in databases with multiple schemas. |
|
table_name |
StrippedNonEmptyStr
|
Name of the partitioned table (lowercase, max 63 chars minus the longest generated partition suffix). |
partition_type |
PartitionType
|
Type of partitioning (RANGE, LIST, HASH). |
partition_strategy |
PartitionStrategy
|
Strategy for partitioning. |
partition_column |
StrippedNonEmptyStr
|
The leading column of the table's partition key. For a time-based table this is the time dimension. |
trailing_partition_columns |
tuple[StrippedNonEmptyStr, ...]
|
The rest of a composite partition key, in key order; empty for the usual single-column case. Trailing columns are bounded with MINVALUE at both ends, so each partition covers exactly one period -- but only for rows whose trailing columns are all non-NULL. PostgreSQL adds an IS NOT NULL test for every key column, so a NULL in any of them routes the row to DEFAULT whatever its period, and DEFAULT is never pruned. Declare them NOT NULL unless you want that. |
granularity |
PartitionGranularity | None
|
Time granularity (for TIME_BASED strategy). |
create_ahead_count |
PositiveInt
|
Number of periods to ensure exist, including the current period. |
retention_count |
PositiveInt
|
Number of partitions to retain. Counted in top-level time periods, never in subpartitions - the time dimension is the lifecycle dimension. |
auto_attach_after_create |
bool
|
Whether to attach immediately after creation. |
root_layout |
SubpartitionSpec | None
|
For a HASH_BASED or VALUE_BASED root, the fixed set of
partitions the table itself is divided into. Such a table has no
time dimension, so it has no create-ahead window and nothing ages
out of it — maintenance only converges the set. Must be |
subpartition |
SubpartitionSpec | None
|
Optional subpartitioning applied inside each partition,
making it a partitioned table in its own right (for example
|
Source code in pg_partsmith/entities.py
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | |
db_schema
property
¶
PostgreSQL schema name.
is_time_based
property
¶
True when this table's partitions come from a calendar period.
A time-based table has a create-ahead window and a retention window; a static one — HASH or LIST at the root — has a fixed set of partitions that neither grows with the clock nor ages out.
key_arity
property
¶
Number of columns in the table's partition key.
partition_columns
property
¶
The table's whole partition key, in key order.
For a time-based table the leading column is the time dimension; trailing columns are bounded with MINVALUE at both ends.
subpartition_levels
property
¶
Every declared level, outermost first.
For a static root that starts with :attr:root_layout itself; for a
time-based one the root is the period dimension, which is not a spec, so
the list starts below it.
validate_identifier(v)
classmethod
¶
Validate and normalise SQL identifiers.
Source code in pg_partsmith/entities.py
validate_schema(v)
classmethod
¶
validate_strategy_requirements()
¶
Validate strategy-specific requirements.
Source code in pg_partsmith/entities.py
validate_subpartitioning()
¶
Reject subpartitioning the library cannot manage on this root.
Defence in depth rather than a reachable path today: a static root
rejects subpartition earlier and points at root_layout, and a
TIME_BASED root already has to be RANGE. Kept so that adding a strategy
cannot open the case silently.
Source code in pg_partsmith/entities.py
validate_trailing_partition_columns(v)
classmethod
¶
Validate and normalise the rest of the partition key.
Source code in pg_partsmith/entities.py
Bases: BaseModel
Result of partition maintenance operation.
Attributes:
| Name | Type | Description |
|---|---|---|
created_count |
NonNegativeInt
|
Number of top-level partitions created. A subpartitioned branch counts once, however many leaves it contains - the branch is the lifecycle unit. |
repaired_count |
NonNegativeInt
|
Number of subpartitions created inside pre-existing branches to close gaps in their child sets. |
detached_count |
NonNegativeInt
|
Number of partitions detached in this run. |
dropped_count |
NonNegativeInt
|
Number of partitions dropped. |
duration_ms |
NonNegativeInt
|
Duration of maintenance in milliseconds. |
error |
str | None
|
Fatal error message (set when the whole maintenance run fails). |
issues |
tuple[MaintenanceIssue, ...]
|
Non-fatal problems. Step failures land here when the run was
started with |
Source code in pg_partsmith/entities.py
success
property
¶
True only when there is no fatal error (non-fatal issues may exist).
Bases: BaseModel
A non-fatal problem recorded during a maintenance run.
Attributes:
| Name | Type | Description |
|---|---|---|
step |
MaintenanceIssueStep
|
Lifecycle step the problem occurred in. |
error |
StrippedNonEmptyStr
|
Error message ( |
partition_name |
str | None
|
Partition the problem concerns, when it is specific to one - subpartition reconciliation always sets it. |
Source code in pg_partsmith/entities.py
Partition topology¶
Bounds, subpartition specs, and the introspected tree.
PartitionBounds is the discriminated union of every bound below
(RangeBounds | ListBounds | HashBounds | DefaultBounds); SubpartitionBounds
is the narrower one a subpartition can be attached with
(HashBounds | ListBounds | DefaultBounds), which excludes RANGE because that
belongs to the time dimension at the root.
Bases: SubpartitionSpecBase
Divide each partition of the level above into HASH buckets.
modulus is the bucket count for newly created branches only. Existing
branches keep the modulus they were built with — a hash set cannot change
modulus without a rewrite — so lowering or raising it changes future
periods and leaves history alone. See the reconciliation guide.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
Literal['hash']
|
Discriminator; always |
modulus |
PositiveInt
|
Number of hash buckets to create per branch. |
name_suffix |
str
|
Must contain |
Source code in pg_partsmith/topology.py
columns
property
¶
The whole partition key of this level, in key order.
partition_type
property
¶
PostgreSQL partition type this spec describes.
bounds_for(remainder)
¶
child_name(parent_relname, remainder)
¶
Return the bare relation name of one bucket under parent_relname.
depth()
¶
name_length_budget()
¶
Bytes this level and everything below it add to a partition name.
Used to keep generated names inside PostgreSQL's 63-byte identifier limit, which truncates silently — two children could otherwise collapse onto one name.
Source code in pg_partsmith/topology.py
own_name_budget()
¶
validate_column(v)
classmethod
¶
Validate and normalise the leading partition key identifier.
validate_depth()
¶
Bound the tree depth so a typo cannot fan out into thousands of tables.
Source code in pg_partsmith/topology.py
validate_key_is_distinct()
¶
A column repeated in the key would leave one position doing nothing.
Source code in pg_partsmith/topology.py
validate_name_suffix(v)
classmethod
¶
Reject templates that could not produce a safe, unique identifier.
Source code in pg_partsmith/topology.py
validate_trailing_columns(v)
classmethod
¶
Validate and normalise the rest of the partition key.
Source code in pg_partsmith/topology.py
walk()
¶
Return this spec and every spec below it, outermost first.
Source code in pg_partsmith/topology.py
Bases: SubpartitionSpecBase
Divide each partition of the level above into named LIST partitions.
Unlike HASH, a LIST level is never "complete": there is always another
value the world could produce. That is what include_default is for — a
catch-all partition so an unknown value is stored rather than rejected.
Because groups are matched by the values they own rather than by name, a tree built by another tool is recognised and left alone instead of being duplicated under different names.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
Literal['list']
|
Discriminator; always |
groups |
tuple[ListGroup, ...]
|
The partitions to maintain, each owning an explicit value set. |
include_default |
bool
|
Maintain a DEFAULT catch-all partition alongside them. |
default_name |
StrippedNonEmptyStr
|
Identifier fragment for that DEFAULT partition. |
name_suffix |
str
|
Must contain |
Source code in pg_partsmith/topology.py
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | |
columns
property
¶
The whole partition key of this level, in key order.
partition_type
property
¶
PostgreSQL partition type this spec describes.
child_name(parent_relname, name)
¶
Return the bare relation name of one child under parent_relname.
depth()
¶
name_length_budget()
¶
Bytes this level and everything below it add to a partition name.
Used to keep generated names inside PostgreSQL's 63-byte identifier limit, which truncates silently — two children could otherwise collapse onto one name.
Source code in pg_partsmith/topology.py
own_name_budget()
¶
Bytes this level adds, sized for the longest group name.
Source code in pg_partsmith/topology.py
validate_column(v)
classmethod
¶
Validate and normalise the leading partition key identifier.
validate_default_name(v)
classmethod
¶
Keep the DEFAULT partition's fragment safe to splice into a name.
validate_depth()
¶
Bound the tree depth so a typo cannot fan out into thousands of tables.
Source code in pg_partsmith/topology.py
validate_groups()
¶
Reject a spec PostgreSQL would refuse or that names two partitions alike.
Source code in pg_partsmith/topology.py
validate_key_is_distinct()
¶
A column repeated in the key would leave one position doing nothing.
Source code in pg_partsmith/topology.py
validate_name_suffix(v)
classmethod
¶
Reject templates that could not produce a safe, unique identifier.
Source code in pg_partsmith/topology.py
validate_trailing_columns(v)
classmethod
¶
Validate and normalise the rest of the partition key.
Source code in pg_partsmith/topology.py
walk()
¶
Return this spec and every spec below it, outermost first.
Source code in pg_partsmith/topology.py
Bases: BaseModel
One named LIST partition and the key values it owns.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
StrippedNonEmptyStr
|
Identifier fragment used to name the partition. |
values |
tuple[StrippedNonEmptyStr, ...]
|
Values routed to it. Rendered as SQL string literals, which PostgreSQL coerces to the partition key's type, so numeric and textual keys are both written as strings here. |
Source code in pg_partsmith/topology.py
bounds()
¶
validate_name(v)
classmethod
¶
validate_values()
¶
A LIST partition owning no values could never route a row.
Source code in pg_partsmith/topology.py
Bases: BaseModel
Fields and tree arithmetic shared by every subpartitioning strategy.
A key is spelled as one leading column plus an optional tail, rather than a
single tuple, so that column stays an ordinary field: it can be read
without ever raising, and model_copy(update={"column": ...}) does what
it says. :attr:columns derives the whole key from the two.
Attributes:
| Name | Type | Description |
|---|---|---|
column |
StrippedNonEmptyStr
|
The leading column this level partitions on. |
trailing_columns |
tuple[StrippedNonEmptyStr, ...]
|
The rest of the key, in key order; empty for the usual single-column case. Every column named here and above must be part of every UNIQUE/PRIMARY KEY constraint on the root table, or PostgreSQL refuses the subtree. |
name_suffix |
str
|
Template appended to the parent's name to name each child. |
subpartition |
SubpartitionSpec | None
|
Optional further level of subpartitioning. |
Source code in pg_partsmith/topology.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | |
columns
property
¶
The whole partition key of this level, in key order.
partition_type
property
¶
PostgreSQL partition type this spec describes.
depth()
¶
name_length_budget()
¶
Bytes this level and everything below it add to a partition name.
Used to keep generated names inside PostgreSQL's 63-byte identifier limit, which truncates silently — two children could otherwise collapse onto one name.
Source code in pg_partsmith/topology.py
own_name_budget()
¶
validate_column(v)
classmethod
¶
Validate and normalise the leading partition key identifier.
validate_depth()
¶
Bound the tree depth so a typo cannot fan out into thousands of tables.
Source code in pg_partsmith/topology.py
validate_key_is_distinct()
¶
A column repeated in the key would leave one position doing nothing.
Source code in pg_partsmith/topology.py
validate_trailing_columns(v)
classmethod
¶
Validate and normalise the rest of the partition key.
Source code in pg_partsmith/topology.py
walk()
¶
Return this spec and every spec below it, outermost first.
Source code in pg_partsmith/topology.py
Bases: BaseModel
One relation in an introspected partition tree.
A node describes both how it sits in its parent (:attr:bounds) and how it
partitions its own children (:attr:partition_type) — the two are
independent, which is exactly what makes a nested tree expressible: a
branch is a partition and a partitioned table at once.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
StrippedNonEmptyStr
|
Schema-qualified relation name. |
parent_name |
StrippedNonEmptyStr | None
|
Schema-qualified parent name; None for the queried root. |
level |
NonNegativeInt
|
Depth below the queried root (0 for the root itself). |
partition_type |
PartitionType | None
|
How this relation partitions its children; None when
it is a plain (leaf) table. Note that
:attr: |
partition_columns |
tuple[str, ...]
|
This relation's own partition key columns. |
bounds |
PartitionBounds | None
|
How this relation is bound inside its parent; None for the root. |
is_attached |
bool
|
|
children |
tuple[PartitionNode, ...]
|
Direct children, ordered by name. |
has_unaddressable_children |
bool
|
Whether a child was left out of
:attr: |
has_expression_key |
bool
|
Whether any position of this relation's own
partition key is an expression rather than a column. Such a
position has no name, so :attr: |
Source code in pg_partsmith/topology.py
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | |
hash_children
property
¶
Children bound by MODULUS/REMAINDER.
is_leaf
property
¶
True when this relation is a plain table that cannot hold partitions.
relname
property
¶
Bare relation name without the schema qualifier.
describe_topology()
¶
Render a one-line summary used in topology diagnostics.
Source code in pg_partsmith/topology.py
find(name)
¶
walk()
¶
Return this node and every node below it, depth-first.
Bases: BaseModel
FOR VALUES FROM (from_value) TO (to_value).
Attributes:
| Name | Type | Description |
|---|---|---|
from_value |
StrippedNonEmptyStr
|
Lower bound, inclusive. |
to_value |
StrippedNonEmptyStr
|
Upper bound, exclusive. |
Source code in pg_partsmith/topology.py
Bases: BaseModel
FOR VALUES WITH (MODULUS modulus, REMAINDER remainder).
A hash partition owns the rows whose key hash is congruent to
remainder modulo modulus; a set of them is complete only when the
owned residue classes tile the whole keyspace (see
:func:hash_keyspace_covered).
Attributes:
| Name | Type | Description |
|---|---|---|
modulus |
PositiveInt
|
Number of buckets this partition's residue class is taken from. |
remainder |
NonNegativeInt
|
Residue this partition owns; always |
Source code in pg_partsmith/topology.py
validate_remainder_in_range()
¶
Reject a remainder outside [0, modulus) — PostgreSQL would too.
Source code in pg_partsmith/topology.py
Bases: BaseModel
FOR VALUES IN (values…).
Attributes:
| Name | Type | Description |
|---|---|---|
values |
tuple[str, ...]
|
The literal values routed to this partition. |
includes_null |
bool
|
Whether |
Source code in pg_partsmith/topology.py
Bases: BaseModel
DEFAULT — the catch-all partition of a RANGE or LIST parent.
Source code in pg_partsmith/topology.py
Shape of a PostgreSQL partition tree: bounds, subpartition specs, and nodes.
Everything here is IO-free and shared by the aio and sync mirrors:
- :class:
PartitionType— how a relation partitions its children. *Bounds— how a relation is bound inside its parent, as PostgreSQL renders it (FOR VALUES FROM … TO …/WITH (MODULUS … REMAINDER …)/IN (…)/DEFAULT).- :class:
HashSubpartitionSpec— the subpartitioning a user asks for. - :class:
PartitionNode— the tree that actually exists, as introspected frompg_partition_treeand friends.
The planner that turns the difference between the last two into DDL intentions
lives in :mod:pg_partsmith.subpartition_plan.
uniform_modulus(bounds)
¶
Return the single modulus shared by bounds, or None when they differ.
An empty set has no modulus and also returns None; callers distinguish the
two cases by checking bounds themselves.
Source code in pg_partsmith/topology.py
hash_keyspace_covered(bounds)
¶
True when bounds tile the whole hash keyspace.
PostgreSQL allows hash siblings at different moduli as long as their
residue classes do not overlap — (2, 1) and (4, 0) coexist happily.
Such a set is complete only if every residue modulo the least common
multiple of the moduli is owned by someone; a gap means rows hashing there
are rejected outright with a check violation, so it must be detected rather
than assumed.
Returns:
| Type | Description |
|---|---|
bool | None
|
True/False, or None when the moduli are too coarse to enumerate within |
bool | None
|
data: |
bool | None
|
unknown and must not be guessed at). |
Source code in pg_partsmith/topology.py
missing_remainders(modulus, bounds)
¶
Return the remainders at modulus that bounds do not already own.
Source code in pg_partsmith/topology.py
Subpartition reconciliation¶
Bases: BaseModel
What to create under one branch, and what was deliberately left alone.
Attributes:
| Name | Type | Description |
|---|---|---|
actions |
tuple[SubpartitionAction, ...]
|
Partitions to create, nested parent-before-child. |
findings |
tuple[TopologyFinding, ...]
|
Divergences the planner refused to repair automatically. |
Source code in pg_partsmith/subpartition_plan.py
Bases: BaseModel
One partition to create, together with the subtree to build inside it.
Executed depth-first: create child_name detached, build its own
children, and only then attach it to parent_name. A subtree
therefore becomes reachable from the parent only once it is complete, so a
crash mid-way can never expose a branch that rejects rows.
Attributes:
| Name | Type | Description |
|---|---|---|
parent_name |
StrippedNonEmptyStr
|
Schema-qualified relation the new partition attaches to. |
child_name |
StrippedNonEmptyStr
|
Schema-qualified name of the partition to create. |
bounds |
SubpartitionBounds
|
Bounds to attach |
subpartition |
SubpartitionSpec | None
|
How |
children |
tuple[SubpartitionAction, ...]
|
Partitions to create inside |
Source code in pg_partsmith/subpartition_plan.py
Bases: BaseModel
Outcome of converging one or more branches towards their spec.
Attributes:
| Name | Type | Description |
|---|---|---|
created_count |
NonNegativeInt
|
Subpartitions actually attached during this run. |
findings |
tuple[TopologyFinding, ...]
|
Divergences the planner refused to repair automatically. |
Source code in pg_partsmith/subpartition_plan.py
merge(other)
¶
Combine two results, preserving finding order.
Source code in pg_partsmith/subpartition_plan.py
Bases: BaseModel
Something the planner observed and chose not to change.
Attributes:
| Name | Type | Description |
|---|---|---|
partition_name |
StrippedNonEmptyStr
|
Schema-qualified name of the branch concerned. |
reason |
TopologyReason
|
Which convergence rule applied. |
detail |
StrippedNonEmptyStr
|
Human-readable explanation, safe to log or surface verbatim. |
Source code in pg_partsmith/subpartition_plan.py
is_actionable
property
¶
True when an operator has to do something about this finding.
Bases: StrEnum
Why the planner left an existing subtree alone.
Attributes:
| Name | Type | Description |
|---|---|---|
LEGACY_LEAF |
The branch is a plain table created before the current subpartitioning policy. PostgreSQL cannot add partitions to it. |
|
STRATEGY_MISMATCH |
The branch is subpartitioned by a different strategy than the config asks for. |
|
COLUMN_MISMATCH |
The branch is subpartitioned by the right strategy but on a different column. |
|
MODULUS_PRESERVED |
The branch has a complete hash set at a modulus the config no longer uses. It already tiles the keyspace, so it stays. |
|
MODULUS_REPAIRED |
The branch has an incomplete hash set at a modulus the config no longer uses; the gaps were filled at the branch's own modulus, which is the only modulus that cannot overlap it. |
|
NON_UNIFORM_COMPLETE |
Hash siblings disagree on modulus but still tile the keyspace — legal, and left untouched. |
|
NON_UNIFORM_INCOMPLETE |
Hash siblings disagree on modulus and leave a gap. Rows hashing into it are rejected, and no repair is provably safe, so this needs a human. |
|
COVERAGE_UNKNOWN |
The moduli are too coarse to enumerate, so coverage could not be verified. |
|
LIST_VALUES_CONFLICT |
A configured LIST group claims a value another partition already owns. A value belongs to exactly one partition, so this needs a human. |
|
NAME_UNUSABLE |
The partition the configuration asks for cannot be given a usable name -- the name is taken by a relation that does not match it, or it exceeds PostgreSQL's identifier limit. |
|
DEFAULT_HOLDS_ROWS |
A DEFAULT sibling holds rows belonging to the partition being created, so PostgreSQL refuses to attach it until they move. |
|
UNCONVERGEABLE |
Converging this branch failed outright; the rest of the table was still maintained. |
Source code in pg_partsmith/subpartition_plan.py
Desired-vs-actual planning for subpartitioned branches.
Pure and IO-free, so one implementation serves both the aio and sync mirrors and every convergence rule is unit-testable without a database.
The planner never mutates and never guesses. Given the subpartitioning a config asks for and the subtree that actually exists, it returns:
- :attr:
SubpartitionPlan.actions— the nodes that are safe to create, nested so a branch is only attached once its own children exist, and - :attr:
SubpartitionPlan.findings— everything it deliberately refused to touch, each with the reason a human needs to act on it.
The refusals matter as much as the creations. A hash set cannot change modulus online, an existing partition may predate the current policy, and a partition that PostgreSQL is happy with must never be "fixed" into one it would reject.
plan_subpartitions(spec, node)
¶
Plan the DDL that converges node's subtree towards spec.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
SubpartitionSpec
|
The subpartitioning the config asks for at |
required |
node
|
PartitionNode
|
The branch as it currently exists, with its children populated. |
required |
Returns:
| Type | Description |
|---|---|
SubpartitionPlan
|
A plan whose actions are safe to execute in order, plus findings for |
SubpartitionPlan
|
everything left untouched. |
Source code in pg_partsmith/subpartition_plan.py
plan_new_subtree(spec, branch_name, findings=None)
¶
Plan the complete subtree of a branch that does not exist yet.
Used on the creation path, where there is nothing to reconcile against and every child the spec describes has to be built.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
SubpartitionSpec
|
The subpartitioning to materialise. |
required |
branch_name
|
str
|
Schema-qualified name of the branch being created. |
required |
findings
|
list[TopologyFinding] | None
|
Collector for children the planner refuses to name. Pass one: without it a refusal is invisible, and a branch planned with fewer children than the spec asks for is a branch that rejects rows. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[SubpartitionAction, ...]
|
Actions creating every child described by |
Source code in pg_partsmith/subpartition_plan.py
Boundary codecs¶
Bases: Protocol
Translates between instants and the literals a RANGE partition is bound by.
Implement this to partition by any time-sortable key. The only contract is
that :meth:encode is monotonic in its argument and :meth:decode inverts
it closely enough for retention comparisons — adjacent periods must produce
contiguous [lower, upper) literals with no gap and no overlap, or rows
fall through into the DEFAULT partition.
Source code in pg_partsmith/boundaries.py
decode(literal)
¶
Decode a catalog boundary literal back to a UTC instant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
literal
|
str
|
A boundary as read from |
required |
Returns:
| Type | Description |
|---|---|
datetime | None
|
The instant the literal stands for, or None when it carries no |
datetime | None
|
instant ( |
Source code in pg_partsmith/boundaries.py
encode(start, end)
¶
Encode a half-open period into (from_value, to_value) literals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
datetime
|
Period start, inclusive; timezone-aware. |
required |
end
|
datetime
|
Period end, exclusive; timezone-aware. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str]
|
The literals to use in |
Source code in pg_partsmith/boundaries.py
Encodes periods as the smallest UUIDv7 of each boundary instant.
UUIDv7 (RFC 9562) puts a 48-bit big-endian Unix-milliseconds timestamp in its leading bits, so UUIDv7 values sort chronologically and a table keyed by one can be RANGE-partitioned by time.
Both boundaries use the minimum UUID for their instant — every random bit zero. Using the minimum on both ends is what makes adjacent periods exactly contiguous: one period's upper bound is the next period's lower bound, so no identifier can fall between two partitions.
Timestamps are truncated to milliseconds, matching UUIDv7's own resolution. Period boundaries are whole hours or larger, so this never loses a boundary.
Source code in pg_partsmith/boundaries.py
decode(literal)
¶
Return the instant encoded in a UUIDv7 literal, or None.
Non-UUID literals (MINVALUE, MAXVALUE, anything the catalog
renders for a differently-typed key) and UUIDs of another version
decode to None rather than raising, so a mixed-history table can still
be introspected.
Source code in pg_partsmith/boundaries.py
encode(start, end)
¶
Return the minimum UUIDv7 for each boundary instant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
datetime
|
Period start, inclusive. |
required |
end
|
datetime
|
Period end, exclusive. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str]
|
Canonical UUID strings for the two boundaries. |
Source code in pg_partsmith/boundaries.py
min_uuid_for(instant)
¶
Return the smallest valid UUIDv7 whose timestamp is instant.
Deterministic: every bit outside the timestamp, version, and variant fields is zero, so the same instant always yields the same boundary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instant
|
datetime
|
A timezone-aware datetime; naive values are read as UTC. |
required |
Returns:
| Type | Description |
|---|---|
UUID
|
The minimum UUIDv7 for that millisecond. |
Source code in pg_partsmith/boundaries.py
Bases: Protocol
Calculator that can read its own physical boundary literals back.
Retention selects partitions by comparing a partition's catalog upper bound against the cutoff instant. When the partition key is not a timestamp — a UUIDv7, a ULID, an epoch bigint — that comparison is only possible if the component that encoded the boundary can also decode it. Calculators without this capability keep the historical timestamp interpretation.
Source code in pg_partsmith/protocols.py
Exceptions¶
Domain exceptions for partition management.
PartitionError
¶
PartitionAlreadyExistsError
¶
Bases: PartitionError
Raised when attempting to create a partition that already exists.
Source code in pg_partsmith/exceptions.py
PartitionNotFoundError
¶
Bases: PartitionError
Raised when a partition is not found.
Source code in pg_partsmith/exceptions.py
PartitionAttachedError
¶
Bases: PartitionError
Raised when attempting to drop an attached partition.
Source code in pg_partsmith/exceptions.py
PartitionDetachInProgressError
¶
Bases: PartitionError
Raised when detach operation is in progress.
Source code in pg_partsmith/exceptions.py
InvalidPartitionConfigError
¶
Bases: PartitionError
Raised when partition configuration is invalid.
Source code in pg_partsmith/exceptions.py
LockAcquisitionError
¶
Bases: PartitionError
Raised when unable to acquire lock for partition operation.
Source code in pg_partsmith/exceptions.py
DropRetryExhaustedError
¶
Bases: PartitionError
Raised when all drop_partition retry attempts are exhausted.
This means PostgreSQL returned a retryable error (deadlock, lock timeout,
or query cancellation) on every attempt. Inspect cause for the last
underlying error.
Source code in pg_partsmith/exceptions.py
UnmanagedPartitionDropError
¶
Bases: PartitionError
Raised when attempting to drop a table not managed by this library.
Source code in pg_partsmith/exceptions.py
PartitionTopologyError
¶
Bases: PartitionError
Raised when an existing partition tree diverges from the configured one.
Carries the planner's finding verbatim so callers can branch on
:attr:reason instead of matching on message text. Reconciliation records
these on MaintenanceResult.issues rather than raising, because one
historical branch with an unexpected shape must not abort maintenance for
every other partition.
Source code in pg_partsmith/exceptions.py
UnsupportedCapabilityError
¶
Bases: PartitionError
Raised when a config is wired to components that cannot serve it.
Custom repositories and metadata providers written against the flat protocols keep working for flat configs; they are only refused when a config actually asks for something they do not implement. The capability is named rather than assumed, because more than one of them is optional.
Source code in pg_partsmith/exceptions.py
Period strategies¶
Bases: ABC
Base class for all period calculators.
Implements common logic for period calculations and defines the interface for granularity-specific strategies. Subclass and override any method to customise behaviour.
Periods are computed in the calculator's timezone (UTC by default): the
current period is derived from "now" in that zone, and naive boundary
literals mean period starts in that zone. Keep the repository's
ddl_timezone aligned with it — PartitionLifecycleService refuses a
mismatched pair.
Subclasses must define _NAME_PATTERN (a compiled regex) and implement
_period_from_match to construct a Period from regex groups.
Group 1 is conventionally the table name; subsequent groups encode the period.
Passing boundary_codec decouples the semantic period from the physical
partition key: periods, names, create-ahead and retention keep working in
calendar terms while the FOR VALUES FROM … TO … literals are whatever
the key actually stores (a UUIDv7, a sortable id). Without one, boundaries
are rendered as the calendar literals they have always been.
Source code in pg_partsmith/strategies/base.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
abstractmethod
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
abstractmethod
¶
get_boundaries(period)
abstractmethod
¶
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for hourly partitions.
Generates partitions with hour granularity. UTC only: in a zone with DST
a local hour can repeat or vanish, making {table}__YYYY_MM_DD_HH names
ambiguous, so non-UTC timezones are rejected.
Partition naming: {table}__{YYYY}_{MM}_{DD}_{HH}
Source code in pg_partsmith/strategies/hour.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator; only tz=datetime.UTC is accepted.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/hour.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
Format partition name: table__YYYY_MM_DD_HH.
Source code in pg_partsmith/strategies/hour.py
get_boundaries(period)
¶
Get hour boundaries as (start, end) UTC timestamps with hour precision.
Source code in pg_partsmith/strategies/hour.py
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for daily partitions.
Generates partitions with day granularity.
Partition naming: {table}__{YYYY}_{MM}_{DD}
Source code in pg_partsmith/strategies/day.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
Format partition name: table__YYYY_MM_DD.
Source code in pg_partsmith/strategies/day.py
get_boundaries(period)
¶
Get day boundaries as (start_date, end_date) in ISO format.
Source code in pg_partsmith/strategies/day.py
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for weekly partitions.
Generates partitions with ISO-week granularity.
Partition naming: {table}__{YYYY}_w{WW}
Source code in pg_partsmith/strategies/week.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
Format partition name: table__YYYY_wWW.
Source code in pg_partsmith/strategies/week.py
get_boundaries(period)
¶
Get ISO-week boundaries (Monday to Monday) as ISO date strings.
Source code in pg_partsmith/strategies/week.py
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for monthly partitions.
Generates partitions with month granularity.
Partition naming: {table}__{YYYY}_{MM}
Source code in pg_partsmith/strategies/month.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
Format partition name: table__YYYY_MM.
Source code in pg_partsmith/strategies/month.py
get_boundaries(period)
¶
Get month boundaries as (start_date, end_date) in ISO format.
Source code in pg_partsmith/strategies/month.py
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for quarterly partitions.
Generates partitions with quarter granularity.
Partition naming: {table}__{YYYY}_q{Q}
Source code in pg_partsmith/strategies/quarter.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
Format partition name: table__YYYY_qQ.
Source code in pg_partsmith/strategies/quarter.py
get_boundaries(period)
¶
Get quarter boundaries as (start_date, end_date) in ISO format.
Source code in pg_partsmith/strategies/quarter.py
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Bases: BasePeriodCalculator
Calculator for yearly partitions.
Generates partitions with year granularity.
Partition naming: {table}__{YYYY}
Source code in pg_partsmith/strategies/year.py
boundary_codec
property
¶
Codec used to render and read physical boundary literals, if any.
timezone_name
property
¶
IANA name of :attr:tz, usable in SET LOCAL TIME ZONE.
tz
property
¶
Timezone the calculator works in.
__init__(tz=UTC, *, boundary_codec=None)
¶
Initialize calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in. Only |
UTC
|
boundary_codec
|
RangeBoundaryCodec | None
|
Optional encoder for the physical partition key. When set, period boundaries are encoded through it instead of being rendered as calendar literals. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/strategies/base.py
current_period()
¶
decode_boundary(literal)
¶
Return the instant a catalog boundary literal stands for, or None.
Retention compares partitions by their upper bound, so whatever encoded a boundary has to be able to read it back. Falls back to interpreting the literal as a timestamp when no codec is configured.
Source code in pg_partsmith/strategies/base.py
format_partition_name(table_name, period)
¶
get_boundaries(period)
¶
Get year boundaries as (start_date, end_date) in ISO format.
next_periods(count)
¶
Generate N periods starting from the current period (inclusive).
Source code in pg_partsmith/strategies/base.py
parse_partition_name(partition_name)
¶
Parse period from a partition name.
Returns None if the name does not match _NAME_PATTERN or encodes
an invalid calendar value (e.g. month 13).
Source code in pg_partsmith/strategies/base.py
period_after(reference, offset)
¶
Return the period offset steps after reference.
Source code in pg_partsmith/strategies/base.py
period_before(reference, offset)
¶
Return the period offset steps before reference.
Source code in pg_partsmith/strategies/base.py
period_start(period)
¶
Return the instant a period begins, in the calculator's timezone.
Period.to_datetime pins UTC; a calculator working in a business
timezone means the same calendar period starts at a different instant,
which is what a boundary codec has to encode.
Source code in pg_partsmith/strategies/base.py
Return the period calculator for the given granularity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
granularity
|
PartitionGranularity
|
The partition time granularity. |
required |
tz
|
tzinfo
|
Timezone the calculator works in ( |
UTC
|
Returns:
| Type | Description |
|---|---|
BasePeriodCalculator
|
A fresh calculator instance for the requested granularity. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If granularity has no registered calculator, or |
Source code in pg_partsmith/strategies/selector.py
pg_partsmith.aio¶
Async implementations: service, maintainer, repositories, lock managers, and hooks.
Service and maintainer¶
Service for managing the full partition lifecycle.
Orchestrates partition creation, detachment, and deletion by delegating to specialized component services.
Source code in pg_partsmith/aio/service.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | |
__init__(repo, metadata, locks, period_calculator=None, hooks=None)
¶
Initialize the partition lifecycle service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repo
|
PartitionRepository
|
DDL operations on partitions (create / attach / detach / drop). |
required |
metadata
|
PartitionMetadataProvider
|
Read-only access to PostgreSQL catalog data. |
required |
locks
|
LockManager
|
Distributed lock manager preventing concurrent maintenance runs. |
required |
period_calculator
|
PeriodCalculator[Period] | None
|
Strategy for determining partition names and boundaries. Required for a TIME_BASED table and meaningless for a static HASH_BASED / VALUE_BASED one, which has no periods. |
None
|
hooks
|
list[PartitionLifecycleHooks] | None
|
Optional list of lifecycle hooks called around each step. |
None
|
Source code in pg_partsmith/aio/service.py
create_future_partitions(config)
async
¶
Create partitions for future periods.
Ensures partitions exist for the next config.create_ahead_count periods
starting from the current period (inclusive). Idempotent: existing partitions
are skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
List of newly created partitions (empty if all already existed). |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a partition exists with conflicting boundaries. |
InvalidPartitionConfigError
|
If |
Source code in pg_partsmith/aio/service.py
detach_old_partitions(table_name, partitions)
async
¶
Detach attached partitions from their parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Qualified parent table name. |
required |
partitions
|
list[PartitionInfo]
|
Attached partitions to detach. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Names of successfully detached partitions. |
Raises:
| Type | Description |
|---|---|
PartitionDetachInProgressError
|
If a concurrent detach is in progress. |
Source code in pg_partsmith/aio/service.py
drop_detached_partitions(table_name, partition_names)
async
¶
Drop previously detached, marker-tagged partitions.
Attached partitions are skipped with a warning (they raise
PartitionAttachedError internally). Unmanaged tables are refused
unless the underlying repository is configured otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Qualified parent table name (used for hook context). |
required |
partition_names
|
list[str]
|
Names of partitions to drop. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of partitions actually dropped. |
Source code in pg_partsmith/aio/service.py
ensure_partition(config, period)
async
¶
Create and attach the partition for one specific period (idempotent).
Unlike :meth:create_future_partitions, targets exactly period —
useful for writers that must guarantee a partition exists before an
insert (e.g. an hourly outbox buffer). Runs the same DEFAULT
reconciliation and attach-race handling as the create-ahead path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
period
|
Period
|
The period the partition must cover. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
The created partition, or None when it already existed (an existing |
PartitionInfo | None
|
detached partition is re-attached when |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/aio/service.py
ensure_partitions(config, periods)
async
¶
Create and attach partitions for an explicit set of periods (idempotent).
The backfill counterpart of :meth:create_future_partitions: the caller
chooses the periods, so data that already sits in the table can be given
partitions without waiting for create-ahead to reach it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
periods
|
Iterable[Period]
|
Periods that must have a partition. Duplicates are ignored; order is preserved. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
The partitions created by this call; periods that already had one |
list[PartitionInfo]
|
are absent from the list. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/aio/service.py
get_partitions_for_pruning(config)
async
¶
Return partitions older than config.retention_count periods.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
Partitions that are eligible for detach + drop, sorted oldest first. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/aio/service.py
maintain_lifecycle(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
async
¶
Run create + detach + drop in a single locked maintenance window.
The whole sequence runs under a single distributed lock acquired through
the configured :class:LockManager, so concurrent maintainers do not
race on the same parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
skip_create
|
bool
|
Skip the create-ahead step. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions (orphans are still dropped). |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures instead of aborting the run:
a failed create still prunes (which may free the space create
needs), a failed detach still drops existing orphans. Failures
are collected into |
False
|
Subpartitioned configs additionally reconcile each branch's bucket set
between create and detach; branches whose shape cannot be converged
safely are reported through MaintenanceResult.issues regardless of
continue_on_error, since leaving them silent would hide writes that
PostgreSQL is rejecting.
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
|
MaintenanceResult
|
because exceptions propagate from this method (the maintainer is |
MaintenanceResult
|
responsible for catching them). |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the table-level maintenance lock is unavailable. |
InvalidPartitionConfigError
|
If |
Source code in pg_partsmith/aio/service.py
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | |
reconcile_subpartitions(config, *, exclude=())
async
¶
Converge the subtree of every attached partition towards the config.
Idempotent and safe to call on its own: it creates only the buckets a branch is genuinely missing, and reports rather than "repairs" any branch whose shape it cannot converge without risk.
It takes no distributed lock of its own -- unlike
:meth:maintain_lifecycle, which runs its whole sequence under one.
Two workers calling this concurrently is safe: a lost race on a bucket
is recognised by its bounds and reported, not retried into a failure.
But calling it while a maintainer is mid-run means both are converging
the same tree, and the wasted work is yours to weigh. Wrap it in your
own lock if you would rather they queued.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. Without a subpartition spec this is a no-op returning an empty result. |
required |
exclude
|
Collection[str]
|
Schema-qualified partition names to skip. |
()
|
Returns:
| Type | Description |
|---|---|
SubpartitionReconcileResult
|
The subpartitions created and the divergences left alone. |
Raises:
| Type | Description |
|---|---|
UnsupportedCapabilityError
|
If the repository or metadata provider cannot serve a nested configuration. |
Source code in pg_partsmith/aio/service.py
Orchestrator for partition lifecycle maintenance.
Wraps a lifecycle service with timing, logging, and error handling.
Operational failures are logged and re-raised by run_maintenance.
Use run_maintenance_safe (or the maintain_partitions helper) when
you need a scheduler-friendly API that always returns MaintenanceResult.
Source code in pg_partsmith/aio/maintainer.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
run_maintenance(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
async
¶
Execute full partition lifecycle maintenance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
skip_create
|
bool
|
Skip creating future partitions. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions. |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures into |
False
|
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
Maintenance result with counts and duration. |
Raises:
| Type | Description |
|---|---|
CancelledError
|
Propagated after being logged. |
Exception
|
Propagated after being logged. |
Source code in pg_partsmith/aio/maintainer.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
run_maintenance_safe(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
async
¶
Run maintenance and always return MaintenanceResult, never raise.
Scheduler-friendly wrapper around :meth:run_maintenance. Any exception
— including asyncio.CancelledError — is captured and reported via
result.error; the duration_ms field always reflects the elapsed
time even on failure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
skip_create
|
bool
|
Skip creating future partitions. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions. |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures into |
False
|
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
|
Source code in pg_partsmith/aio/maintainer.py
Protocols¶
Implement these to swap in your own storage or locking. The flat pair is all a single-column, unnested config needs; the rest are opt-in and only required when a config actually asks for what they add.
Bases: Protocol
Repository for partition DDL operations.
This protocol is intentionally limited to write operations. All read
operations (listing partitions, checking existence) live in
PartitionMetadataProvider so that the two concerns can be mocked,
swapped, or overridden independently.
Source code in pg_partsmith/aio/protocols.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
attach_partition(table_name, partition_name, from_value, to_value)
async
¶
Attach partition to parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Source code in pg_partsmith/aio/protocols.py
create_partition(config, partition_name, from_value, to_value)
async
¶
Create a new partition table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition_name
|
str
|
Name for the new partition table. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo
|
Created partition info. |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If partition already exists. |
Source code in pg_partsmith/aio/protocols.py
detach_partition(table_name, partition_name, *, concurrent=True)
async
¶
Detach partition from parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
concurrent
|
bool
|
Use DETACH PARTITION CONCURRENTLY if supported. |
True
|
Raises:
| Type | Description |
|---|---|
PartitionNotFoundError
|
If partition doesn't exist. |
Source code in pg_partsmith/aio/protocols.py
drop_partition(partition_name)
async
¶
Drop a partition table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Raises:
| Type | Description |
|---|---|
PartitionAttachedError
|
If partition is still attached. |
reconcile_default_rows(*, default_partition_name, target_partition_name, partition_column, from_value, to_value)
async
¶
Move rows from DEFAULT partition to target partition for given range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_partition_name
|
str
|
Qualified name of DEFAULT partition. |
required |
target_partition_name
|
str
|
Qualified name of target partition. |
required |
partition_column
|
str
|
Column used for partitioning. |
required |
from_value
|
str
|
Range start boundary (inclusive). |
required |
to_value
|
str
|
Range end boundary (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of rows moved. |
Raises:
| Type | Description |
|---|---|
SQLAlchemyError
|
On database errors. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
Provider for reading partition metadata from the database catalogue.
This protocol owns all read operations so that the service layer depends on a single injectable read interface. Implement this protocol to support a different database, a caching layer, or a stub for testing.
Source code in pg_partsmith/aio/protocols.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |
get_default_partition(table_name)
async
¶
Get DEFAULT partition for a table if it exists and is attached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
PartitionInfo with is_default=True, or None if no default partition exists. |
Source code in pg_partsmith/aio/protocols.py
get_partition_boundaries(partition_name)
async
¶
get_partition_column(table_name)
async
¶
Get partition column for a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Partition column name or None if table is not partitioned. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table uses a composite (multi-column) partition key. |
Source code in pg_partsmith/aio/protocols.py
get_partition_type(table_name)
async
¶
Get partition type for a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionType | None
|
Partition type or None if table is not partitioned. |
is_partition_attached(table_name, partition_name)
async
¶
Check if a partition is currently attached to its parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the partition is attached via pg_inherits. |
Source code in pg_partsmith/aio/protocols.py
list_partitions(table_name)
async
¶
List all partitions for a table, including orphaned detached ones.
Orphaned partitions are tables that were detached in a previous
maintenance run but never dropped. They are returned with
is_attached=False and None boundaries so that the service can
schedule them for cleanup on the next run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
List of partition metadata. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
DDL for partitions that are themselves partitioned tables.
Kept separate from :class:PartitionRepository on purpose: a repository
written against the flat protocol keeps satisfying it, and is only required
to grow these three methods once a config actually asks for subpartitioning.
Source code in pg_partsmith/aio/protocols.py
attach_subpartition(parent_name, child_name, bounds)
async
¶
Attach one subpartition to its parent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_name
|
str
|
Partitioned relation to attach to. |
required |
child_name
|
str
|
Table to attach. |
required |
bounds
|
SubpartitionBounds
|
What the child owns — a hash bucket, a set of LIST values, or DEFAULT. |
required |
Source code in pg_partsmith/aio/protocols.py
create_branch(config, branch_name, from_value, to_value, spec)
async
¶
Create a detached time partition that is itself partitioned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
branch_name
|
str
|
Name for the new branch table. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
spec
|
SubpartitionSpec
|
Subpartitioning the branch applies to its own children. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo
|
Info about the created (still detached) branch. |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a relation of that name exists. |
Source code in pg_partsmith/aio/protocols.py
create_subpartition_table(parent_name, child_name, spec)
async
¶
Create a detached table shaped like parent_name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_name
|
str
|
Relation the table will later be attached to. |
required |
child_name
|
str
|
Name for the new table. |
required |
spec
|
SubpartitionSpec | None
|
Subpartitioning the table applies to its own children, or None. |
required |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a relation of that name exists. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
Structural introspection a nested configuration needs.
Also separate from :class:PartitionMetadataProvider so flat setups keep
working with providers that predate subpartitioning.
Source code in pg_partsmith/aio/protocols.py
get_partition_tree(table_name)
async
¶
Return the whole partition tree rooted at table_name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Root of the tree, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
PartitionNode | None
|
The root node with its descendants, or None when the relation is |
PartitionNode | None
|
neither partitioned nor a partition. |
Source code in pg_partsmith/aio/protocols.py
get_unique_constraint_columns(table_name)
async
¶
Return the column tuples of every UNIQUE / PRIMARY KEY constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[tuple[str, ...], ...]
|
One tuple of column names per constraint. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
DDL for a parent whose partition key spans several columns.
Separate from :class:PartitionRepository for the same reason as the
nested protocols: a repository written before composite keys keeps
satisfying the flat one, and is only required to grow this method once a
config actually declares a multi-column key.
Source code in pg_partsmith/aio/protocols.py
attach_composite_partition(table_name, partition_name, from_value, to_value, *, key_arity)
async
¶
Attach a partition, padding the trailing key columns with MINVALUE.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
from_value
|
str
|
Start boundary for the leading column. |
required |
to_value
|
str
|
End boundary for the leading column. |
required |
key_arity
|
int
|
Number of columns in the parent's partition key. |
required |
Source code in pg_partsmith/aio/protocols.py
reconcile_default_rows(*, default_partition_name, target_partition_name, partition_column, trailing_columns=(), from_value, to_value)
async
¶
Move conflicting rows from a DEFAULT partition, honouring the whole key.
The composite widening of :meth:PartitionRepository.reconcile_default_rows.
It is declared here rather than on the base protocol so an
implementation written against the single-column signature keeps
satisfying PartitionRepository -- for a type checker as much as at
runtime.
PostgreSQL adds an IS NOT NULL test for every key column to a range partition's constraint, so a row with a NULL trailing key value belongs in DEFAULT and has to be left there.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_partition_name
|
str
|
Qualified name of DEFAULT partition. |
required |
target_partition_name
|
str
|
Qualified name of target partition. |
required |
partition_column
|
str
|
Leading column of the partition key. |
required |
trailing_columns
|
tuple[str, ...]
|
The remaining key columns, in key order. |
()
|
from_value
|
str
|
Range start boundary (inclusive). |
required |
to_value
|
str
|
Range end boundary (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of rows moved. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
Introspection of a partition key that spans several columns.
Source code in pg_partsmith/aio/protocols.py
get_partition_columns(table_name)
async
¶
Return a table's own partition key columns, in key order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
The key columns in order; empty when the table is not partitioned. |
Source code in pg_partsmith/aio/protocols.py
Bases: Protocol
Lock manager for coordinating partition operations.
This protocol defines the interface for acquiring and releasing locks to prevent concurrent partition operations on the same table. Implement this to use a different locking backend (e.g. Zookeeper).
Source code in pg_partsmith/aio/protocols.py
acquire_lock(table_name)
¶
Acquire lock for partition operations on a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name to lock. |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[None]
|
Async context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If unable to acquire lock. |
Source code in pg_partsmith/aio/protocols.py
PostgreSQL implementations¶
PostgreSQL implementation of partition repository.
Facade that delegates to specialized helper classes for improved maintenance and SRP.
Source code in pg_partsmith/aio/repositories/repository.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
ddl_timezone
property
¶
Timezone applied via SET LOCAL TIME ZONE around boundary-sensitive DDL.
None means the session timezone is trusted as-is.
adopt_partition(table_name, partition_name)
async
¶
Mark a detached legacy table as owned by this library (orphan marker).
See :meth:PartitionRemover.adopt. Use once when migrating an existing
partitioner instead of enabling drop_allow_unmanaged.
Source code in pg_partsmith/aio/repositories/repository.py
attach_composite_partition(table_name, partition_name, from_value, to_value, *, key_arity)
async
¶
Attach a partition to a parent with a composite partition key.
See :meth:PartitionCreator.attach_composite_partition.
Source code in pg_partsmith/aio/repositories/repository.py
attach_subpartition(parent_name, child_name, bounds)
async
¶
Attach one subpartition to its parent.
See :meth:PartitionCreator.attach_subpartition.
Source code in pg_partsmith/aio/repositories/repository.py
create_branch(config, branch_name, from_value, to_value, spec)
async
¶
Create a detached time partition that is itself partitioned.
See :meth:PartitionCreator.create_branch. Its buckets are created
separately and the branch is attached last, so an interrupted run can
never leave a partially-covering branch reachable from the root.
Source code in pg_partsmith/aio/repositories/repository.py
create_subpartition_table(parent_name, child_name, spec)
async
¶
Create a detached table shaped like parent_name.
See :meth:PartitionCreator.create_subpartition_table.
Source code in pg_partsmith/aio/repositories/repository.py
Provider for PostgreSQL partition metadata.
Queries pg_catalog to retrieve information about table partitioning. Override any method to customise catalog queries for your schema setup.
Each method opens its own read-only connection from the engine pool so it is safe to call outside any existing transaction.
Source code in pg_partsmith/aio/metadata.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | |
__init__(engine, *, marker_prefix=None, boundary_codec=None, ddl_timezone=None)
¶
Initialize provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
AsyncEngine
|
SQLAlchemy async engine. |
required |
marker_prefix
|
str | None
|
Optional COMMENT marker prefix for orphaned partitions. When None, the library default prefix is used. Pass the same value to both repository and metadata provider if you override it. |
None
|
ddl_timezone
|
str | None
|
Session timezone to read naive boundary literals in.
Pass whatever the repository writes partitions with: a
|
None
|
boundary_codec
|
RangeBoundaryCodec | None
|
Codec used to read boundary literals back into instants. Required only when the partition key is an encoded identifier rather than a timestamp; pass the same codec the period calculator was built with. |
None
|
Source code in pg_partsmith/aio/metadata.py
get_default_partition(table_name)
async
¶
Get DEFAULT partition for a table if it exists and is attached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
PartitionInfo with is_default=True, or None if no default partition exists. |
Source code in pg_partsmith/aio/metadata.py
get_partition_boundaries(partition_name)
async
¶
Get partition boundaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str] | None
|
Tuple of (from_value, to_value) or None if not a range partition. |
Source code in pg_partsmith/aio/metadata.py
get_partition_column(table_name)
async
¶
Get partition column for a table.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table uses a composite (multi-column) partition key. Only single-column keys are supported by this library. |
Source code in pg_partsmith/aio/metadata.py
get_partition_columns(table_name)
async
¶
Return a table's own partition key columns, in key order.
Unlike :meth:get_partition_column, which predates composite keys and
refuses them, this reports the whole key. Key order is not column
order, so it comes from partattrs' own ordering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
The key columns in order; empty when the table is not partitioned. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If any key position is an expression rather than a column, which this library cannot address. |
Source code in pg_partsmith/aio/metadata.py
get_partition_tree(table_name)
async
¶
Return the whole partition tree rooted at table_name.
Unlike :meth:list_partitions, which reports the direct children a
lifecycle acts on, this walks the hierarchy to the leaves — the shape
subpartition reconciliation needs to know which buckets exist. One
round-trip regardless of depth.
Detached partitions are absent by construction: a detached branch is no longer part of its parent's tree. Query it by name to inspect it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Root of the tree, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
PartitionNode | None
|
The root node with its descendants, or None when |
PartitionNode | None
|
not partitioned and is not itself a partition. |
Source code in pg_partsmith/aio/metadata.py
get_partition_type(table_name)
async
¶
Get partition type for a table.
Source code in pg_partsmith/aio/metadata.py
get_unique_constraint_columns(table_name)
async
¶
Return the column tuples of every UNIQUE / PRIMARY KEY constraint.
PostgreSQL requires such a constraint on a partitioned table to contain all of its partition-key columns. Reading them lets a subpartitioning config be refused with an explanation before any DDL is attempted, instead of failing halfway through a maintenance run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
One tuple of column names per constraint; empty when the table has |
...
|
no unique constraints at all. |
Source code in pg_partsmith/aio/metadata.py
is_partition_attached(table_name, partition_name)
async
¶
Check if a partition is currently attached to its parent via pg_inherits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the partition is attached. |
Source code in pg_partsmith/aio/metadata.py
is_partition_closed(partition_name, *, settle_seconds=0)
async
¶
True when the partition's upper bound (+ settle buffer) has passed.
now() is evaluated on the server rather than on the client, so the
answer tolerates app-clock skew. Useful for export/archive pipelines
that must only finalize partitions which can no longer receive
in-range rows.
Works for a subpartitioned branch exactly as for a plain leaf: what is read is the branch's own RANGE bound in the root table, and its whole subtree closes with it.
A naive bound -- which is what a timestamp or date key produces
-- is resolved under this provider's ddl_timezone. Configure it with
the same value the repository writes partitions with, or the two
disagree about when the bound falls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Attached partition table name. |
required |
settle_seconds
|
int
|
Extra buffer after the upper bound for late writers still holding open transactions. |
0
|
Returns:
| Type | Description |
|---|---|
bool
|
True when |
bool
|
DEFAULT partition, non-RANGE partitions, unbounded upper bounds |
bool
|
(MAXVALUE / infinity), detached tables, unresolvable names, and |
bool
|
boundaries that carry no instant this provider can read. |
Source code in pg_partsmith/aio/metadata.py
list_partitions(table_name)
async
¶
List all partitions for a table, including orphaned detached ones.
Orphaned partitions are detached-but-not-dropped tables previously
detached by this library. They are detected by a COMMENT marker set on
successful detach and returned with is_attached=False and None
boundaries.
Partition names are always schema-qualified with the child's catalog
schema — a partition may live in a different schema than its parent,
and a bare name could resolve to an unrelated table via search_path.
Source code in pg_partsmith/aio/metadata.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
partition_exists(partition_name)
async
¶
Check if a partition table exists in pg_class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the table exists as a regular or partitioned table |
bool
|
(a partition may itself be subpartitioned). |
Source code in pg_partsmith/aio/metadata.py
Lock managers¶
Lock manager using PostgreSQL advisory locks.
Holds the advisory lock on a dedicated AUTOCOMMIT connection from the engine pool. This guarantees the lock survives any number of commits or rollbacks on the caller's session, which is required when the caller needs to commit DDL (e.g. ATTACH PARTITION) before running DETACH PARTITION CONCURRENTLY.
Override _compute_lock_id to customise the lock ID derivation.
Source code in pg_partsmith/aio/lock/postgres.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
__init__(engine, prefix=DEFAULT_LOCK_PREFIX, acquire_min_interval_seconds=0.0)
¶
Initialize lock manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
AsyncEngine
|
SQLAlchemy async engine used to open a dedicated connection for the advisory lock. |
required |
prefix
|
str
|
Prefix for lock key generation. |
DEFAULT_LOCK_PREFIX
|
acquire_min_interval_seconds
|
float
|
Minimum seconds between acquire attempts per table (rate limiting). 0 disables. |
0.0
|
Source code in pg_partsmith/aio/lock/postgres.py
acquire_lock(table_name)
¶
Acquire advisory lock for a table.
Opens a dedicated AUTOCOMMIT connection from the engine pool and acquires a session-level advisory lock on it. The lock is released when the context manager exits, with cancellation-safe cleanup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name to lock. |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[None]
|
Async context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the lock cannot be acquired. |
Source code in pg_partsmith/aio/lock/postgres.py
is_locked(table_name)
async
¶
Check if lock is held by any session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the advisory lock for the given table is currently held. |
Source code in pg_partsmith/aio/lock/postgres.py
Lock manager using Redis for distributed coordination.
Uses SET NX EX to acquire the lock and a background renewal task to
extend the TTL while the lock is held, preventing expiry during long DDL
operations (e.g. DETACH PARTITION CONCURRENTLY). The lock is released
atomically via a Lua script that checks the ownership token, so it is safe
even if Redis restarts during the renewal window.
The renewal interval is ttl_seconds // 3 (with random jitter to avoid
thundering herds). If renewal fails — e.g. Redis is unreachable or another
holder takes over — the watchdog logs a warning and cancels the holder
task, forcing the maintenance run to stop (fail-safe).
For production use you may want to subclass and override acquire_lock
to use Redlock or another algorithm with stronger guarantees.
Raises:
| Type | Description |
|---|---|
ImportError
|
If the |
Source code in pg_partsmith/aio/lock/redis.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | |
__init__(redis_client, prefix=_DEFAULT_REDIS_LOCK_PREFIX, ttl_seconds=300, acquire_min_interval_seconds=0.0)
¶
Initialize lock manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redis_client
|
RedisClientProtocol
|
Redis client instance. |
required |
prefix
|
str
|
Prefix for Redis keys. |
_DEFAULT_REDIS_LOCK_PREFIX
|
ttl_seconds
|
int
|
Lock time-to-live in seconds. The lock is automatically
renewed every |
300
|
acquire_min_interval_seconds
|
float
|
Minimum seconds between acquire attempts per table (rate limiting). 0 disables. |
0.0
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
ValueError
|
If |
Source code in pg_partsmith/aio/lock/redis.py
acquire_lock(table_name)
¶
Acquire Redis lock with automatic TTL renewal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[None]
|
Async context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the lock is already held. |
Source code in pg_partsmith/aio/lock/redis.py
is_locked(table_name)
async
¶
Return True if the Redis lock for table_name is currently held.
Hooks¶
No-op base implementation of partition lifecycle hooks.
Subclass and override only the methods you need. All methods are no-ops by default so you can selectively add behaviour without implementing every step.
Source code in pg_partsmith/aio/hooks.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
after_create(config, partition)
async
¶
Called after a partition has been created (and optionally attached).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition
|
PartitionInfo
|
Info about the newly created partition. |
required |
Source code in pg_partsmith/aio/hooks.py
after_detach(table_name, partition_name)
async
¶
after_drop(table_name, partition_name)
async
¶
before_create(config, partition_name, from_value, to_value)
async
¶
Called before a partition is created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition_name
|
str
|
Name the new partition will be given. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Source code in pg_partsmith/aio/hooks.py
before_detach(table_name, partition)
async
¶
Called before a partition is detached from its parent table.
This is a good place to export or archive data while the partition is still accessible via the parent table's indexes and constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition
|
PartitionInfo
|
Info about the partition being detached. |
required |
Source code in pg_partsmith/aio/hooks.py
before_drop(table_name, partition_name)
async
¶
Called before a partition table is dropped.
This is the last chance to read or export data from the partition before it is permanently destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Name of the partition about to be dropped. |
required |
Source code in pg_partsmith/aio/hooks.py
pg_partsmith.sync¶
Synchronous mirror of pg_partsmith.aio: same class names and API, plain methods
built on the sync SQLAlchemy Engine.
Service and maintainer¶
Service for managing the full partition lifecycle.
Orchestrates partition creation, detachment, and deletion by delegating to specialized component services.
Source code in pg_partsmith/sync/service.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | |
__init__(repo, metadata, locks, period_calculator=None, hooks=None)
¶
Initialize the partition lifecycle service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repo
|
PartitionRepository
|
DDL operations on partitions (create / attach / detach / drop). |
required |
metadata
|
PartitionMetadataProvider
|
Read-only access to PostgreSQL catalog data. |
required |
locks
|
LockManager
|
Distributed lock manager preventing concurrent maintenance runs. |
required |
period_calculator
|
PeriodCalculator[Period] | None
|
Strategy for determining partition names and boundaries. Required for a TIME_BASED table and meaningless for a static HASH_BASED / VALUE_BASED one, which has no periods. |
None
|
hooks
|
list[PartitionLifecycleHooks] | None
|
Optional list of lifecycle hooks called around each step. |
None
|
Source code in pg_partsmith/sync/service.py
create_future_partitions(config)
¶
Create partitions for future periods.
Ensures partitions exist for the next config.create_ahead_count periods
starting from the current period (inclusive). Idempotent: existing partitions
are skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
List of newly created partitions (empty if all already existed). |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a partition exists with conflicting boundaries. |
InvalidPartitionConfigError
|
If |
Source code in pg_partsmith/sync/service.py
detach_old_partitions(table_name, partitions)
¶
Detach attached partitions from their parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Qualified parent table name. |
required |
partitions
|
list[PartitionInfo]
|
Attached partitions to detach. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Names of successfully detached partitions. |
Raises:
| Type | Description |
|---|---|
PartitionDetachInProgressError
|
If a concurrent detach is in progress. |
Source code in pg_partsmith/sync/service.py
drop_detached_partitions(table_name, partition_names)
¶
Drop previously detached, marker-tagged partitions.
Attached partitions are skipped with a warning (they raise
PartitionAttachedError internally). Unmanaged tables are refused
unless the underlying repository is configured otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Qualified parent table name (used for hook context). |
required |
partition_names
|
list[str]
|
Names of partitions to drop. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of partitions actually dropped. |
Source code in pg_partsmith/sync/service.py
ensure_partition(config, period)
¶
Create and attach the partition for one specific period (idempotent).
Unlike :meth:create_future_partitions, targets exactly period —
useful for writers that must guarantee a partition exists before an
insert (e.g. an hourly outbox buffer). Runs the same DEFAULT
reconciliation and attach-race handling as the create-ahead path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
period
|
Period
|
The period the partition must cover. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
The created partition, or None when it already existed (an existing |
PartitionInfo | None
|
detached partition is re-attached when |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/sync/service.py
ensure_partitions(config, periods)
¶
Create and attach partitions for an explicit set of periods (idempotent).
The backfill counterpart of :meth:create_future_partitions: the caller
chooses the periods, so data that already sits in the table can be given
partitions without waiting for create-ahead to reach it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
periods
|
Iterable[Period]
|
Periods that must have a partition. Duplicates are ignored; order is preserved. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
The partitions created by this call; periods that already had one |
list[PartitionInfo]
|
are absent from the list. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/sync/service.py
get_partitions_for_pruning(config)
¶
Return partitions older than config.retention_count periods.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
Partitions that are eligible for detach + drop, sorted oldest first. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If the service was built without a period calculator, which every period-driven call needs. |
Source code in pg_partsmith/sync/service.py
maintain_lifecycle(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
¶
Run create + detach + drop in a single locked maintenance window.
The whole sequence runs under a single distributed lock acquired through
the configured :class:LockManager, so concurrent maintainers do not
race on the same parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
skip_create
|
bool
|
Skip the create-ahead step. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions (orphans are still dropped). |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures instead of aborting the run:
a failed create still prunes (which may free the space create
needs), a failed detach still drops existing orphans. Failures
are collected into |
False
|
Subpartitioned configs additionally reconcile each branch's bucket set
between create and detach; branches whose shape cannot be converged
safely are reported through MaintenanceResult.issues regardless of
continue_on_error, since leaving them silent would hide writes that
PostgreSQL is rejecting.
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
|
MaintenanceResult
|
because exceptions propagate from this method (the maintainer is |
MaintenanceResult
|
responsible for catching them). |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the table-level maintenance lock is unavailable. |
InvalidPartitionConfigError
|
If |
Source code in pg_partsmith/sync/service.py
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | |
reconcile_subpartitions(config, *, exclude=())
¶
Converge the subtree of every attached partition towards the config.
Idempotent and safe to call on its own: it creates only the buckets a branch is genuinely missing, and reports rather than "repairs" any branch whose shape it cannot converge without risk.
It takes no distributed lock of its own -- unlike
:meth:maintain_lifecycle, which runs its whole sequence under one.
Two workers calling this concurrently is safe: a lost race on a bucket
is recognised by its bounds and reported, not retried into a failure.
But calling it while a maintainer is mid-run means both are converging
the same tree, and the wasted work is yours to weigh. Wrap it in your
own lock if you would rather they queued.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. Without a subpartition spec this is a no-op returning an empty result. |
required |
exclude
|
Collection[str]
|
Schema-qualified partition names to skip. |
()
|
Returns:
| Type | Description |
|---|---|
SubpartitionReconcileResult
|
The subpartitions created and the divergences left alone. |
Raises:
| Type | Description |
|---|---|
UnsupportedCapabilityError
|
If the repository or metadata provider cannot serve a nested configuration. |
Source code in pg_partsmith/sync/service.py
Orchestrator for partition lifecycle maintenance.
Wraps a lifecycle service with timing, logging, and error handling.
Operational failures are logged and re-raised by run_maintenance.
Use run_maintenance_safe (or the maintain_partitions helper) when
you need a scheduler-friendly API that always returns MaintenanceResult.
Source code in pg_partsmith/sync/maintainer.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
run_maintenance(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
¶
Execute full partition lifecycle maintenance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
skip_create
|
bool
|
Skip creating future partitions. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions. |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures into |
False
|
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
Maintenance result with counts and duration. |
Raises:
| Type | Description |
|---|---|
KeyboardInterrupt
|
Propagated after being logged. |
Exception
|
Propagated after being logged. |
Source code in pg_partsmith/sync/maintainer.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |
run_maintenance_safe(config, *, skip_create=False, skip_detach=False, skip_drop=False, continue_on_error=False)
¶
Run maintenance and always return MaintenanceResult, never raise.
Scheduler-friendly wrapper around :meth:run_maintenance. Any exception
— including KeyboardInterrupt — is captured and reported via
result.error; the duration_ms field always reflects the elapsed
time even on failure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partitioning configuration. |
required |
skip_create
|
bool
|
Skip creating future partitions. |
False
|
skip_detach
|
bool
|
Skip detaching old partitions. |
False
|
skip_drop
|
bool
|
Skip dropping detached partitions. |
False
|
continue_on_error
|
bool
|
Isolate step failures into |
False
|
Returns:
| Type | Description |
|---|---|
MaintenanceResult
|
|
Source code in pg_partsmith/sync/maintainer.py
Protocols¶
Implement these to swap in your own storage or locking. The flat pair is all a single-column, unnested config needs; the rest are opt-in and only required when a config actually asks for what they add.
Bases: Protocol
Repository for partition DDL operations.
This protocol is intentionally limited to write operations. All read
operations (listing partitions, checking existence) live in
PartitionMetadataProvider so that the two concerns can be mocked,
swapped, or overridden independently.
Source code in pg_partsmith/sync/protocols.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
attach_partition(table_name, partition_name, from_value, to_value)
¶
Attach partition to parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Source code in pg_partsmith/sync/protocols.py
create_partition(config, partition_name, from_value, to_value)
¶
Create a new partition table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition_name
|
str
|
Name for the new partition table. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo
|
Created partition info. |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If partition already exists. |
Source code in pg_partsmith/sync/protocols.py
detach_partition(table_name, partition_name, *, concurrent=True)
¶
Detach partition from parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
concurrent
|
bool
|
Use DETACH PARTITION CONCURRENTLY if supported. |
True
|
Raises:
| Type | Description |
|---|---|
PartitionNotFoundError
|
If partition doesn't exist. |
Source code in pg_partsmith/sync/protocols.py
drop_partition(partition_name)
¶
Drop a partition table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Raises:
| Type | Description |
|---|---|
PartitionAttachedError
|
If partition is still attached. |
reconcile_default_rows(*, default_partition_name, target_partition_name, partition_column, from_value, to_value)
¶
Move rows from DEFAULT partition to target partition for given range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_partition_name
|
str
|
Qualified name of DEFAULT partition. |
required |
target_partition_name
|
str
|
Qualified name of target partition. |
required |
partition_column
|
str
|
Column used for partitioning. |
required |
from_value
|
str
|
Range start boundary (inclusive). |
required |
to_value
|
str
|
Range end boundary (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of rows moved. |
Raises:
| Type | Description |
|---|---|
SQLAlchemyError
|
On database errors. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
Provider for reading partition metadata from the database catalogue.
This protocol owns all read operations so that the service layer depends on a single injectable read interface. Implement this protocol to support a different database, a caching layer, or a stub for testing.
Source code in pg_partsmith/sync/protocols.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |
get_default_partition(table_name)
¶
Get DEFAULT partition for a table if it exists and is attached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
PartitionInfo with is_default=True, or None if no default partition exists. |
Source code in pg_partsmith/sync/protocols.py
get_partition_boundaries(partition_name)
¶
get_partition_column(table_name)
¶
Get partition column for a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Partition column name or None if table is not partitioned. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table uses a composite (multi-column) partition key. |
Source code in pg_partsmith/sync/protocols.py
get_partition_type(table_name)
¶
Get partition type for a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionType | None
|
Partition type or None if table is not partitioned. |
is_partition_attached(table_name, partition_name)
¶
Check if a partition is currently attached to its parent table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the partition is attached via pg_inherits. |
Source code in pg_partsmith/sync/protocols.py
list_partitions(table_name)
¶
List all partitions for a table, including orphaned detached ones.
Orphaned partitions are tables that were detached in a previous
maintenance run but never dropped. They are returned with
is_attached=False and None boundaries so that the service can
schedule them for cleanup on the next run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
list[PartitionInfo]
|
List of partition metadata. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
DDL for partitions that are themselves partitioned tables.
Kept separate from :class:PartitionRepository on purpose: a repository
written against the flat protocol keeps satisfying it, and is only required
to grow these three methods once a config actually asks for subpartitioning.
Source code in pg_partsmith/sync/protocols.py
attach_subpartition(parent_name, child_name, bounds)
¶
Attach one subpartition to its parent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_name
|
str
|
Partitioned relation to attach to. |
required |
child_name
|
str
|
Table to attach. |
required |
bounds
|
SubpartitionBounds
|
What the child owns — a hash bucket, a set of LIST values, or DEFAULT. |
required |
Source code in pg_partsmith/sync/protocols.py
create_branch(config, branch_name, from_value, to_value, spec)
¶
Create a detached time partition that is itself partitioned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
branch_name
|
str
|
Name for the new branch table. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
spec
|
SubpartitionSpec
|
Subpartitioning the branch applies to its own children. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo
|
Info about the created (still detached) branch. |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a relation of that name exists. |
Source code in pg_partsmith/sync/protocols.py
create_subpartition_table(parent_name, child_name, spec)
¶
Create a detached table shaped like parent_name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_name
|
str
|
Relation the table will later be attached to. |
required |
child_name
|
str
|
Name for the new table. |
required |
spec
|
SubpartitionSpec | None
|
Subpartitioning the table applies to its own children, or None. |
required |
Raises:
| Type | Description |
|---|---|
PartitionAlreadyExistsError
|
If a relation of that name exists. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
Structural introspection a nested configuration needs.
Also separate from :class:PartitionMetadataProvider so flat setups keep
working with providers that predate subpartitioning.
Source code in pg_partsmith/sync/protocols.py
get_partition_tree(table_name)
¶
Return the whole partition tree rooted at table_name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Root of the tree, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
PartitionNode | None
|
The root node with its descendants, or None when the relation is |
PartitionNode | None
|
neither partitioned nor a partition. |
Source code in pg_partsmith/sync/protocols.py
get_unique_constraint_columns(table_name)
¶
Return the column tuples of every UNIQUE / PRIMARY KEY constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[tuple[str, ...], ...]
|
One tuple of column names per constraint. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
DDL for a parent whose partition key spans several columns.
Separate from :class:PartitionRepository for the same reason as the
nested protocols: a repository written before composite keys keeps
satisfying the flat one, and is only required to grow this method once a
config actually declares a multi-column key.
Source code in pg_partsmith/sync/protocols.py
attach_composite_partition(table_name, partition_name, from_value, to_value, *, key_arity)
¶
Attach a partition, padding the trailing key columns with MINVALUE.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
from_value
|
str
|
Start boundary for the leading column. |
required |
to_value
|
str
|
End boundary for the leading column. |
required |
key_arity
|
int
|
Number of columns in the parent's partition key. |
required |
Source code in pg_partsmith/sync/protocols.py
reconcile_default_rows(*, default_partition_name, target_partition_name, partition_column, trailing_columns=(), from_value, to_value)
¶
Move conflicting rows from a DEFAULT partition, honouring the whole key.
The composite widening of :meth:PartitionRepository.reconcile_default_rows.
It is declared here rather than on the base protocol so an
implementation written against the single-column signature keeps
satisfying PartitionRepository -- for a type checker as much as at
runtime.
PostgreSQL adds an IS NOT NULL test for every key column to a range partition's constraint, so a row with a NULL trailing key value belongs in DEFAULT and has to be left there.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_partition_name
|
str
|
Qualified name of DEFAULT partition. |
required |
target_partition_name
|
str
|
Qualified name of target partition. |
required |
partition_column
|
str
|
Leading column of the partition key. |
required |
trailing_columns
|
tuple[str, ...]
|
The remaining key columns, in key order. |
()
|
from_value
|
str
|
Range start boundary (inclusive). |
required |
to_value
|
str
|
Range end boundary (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of rows moved. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
Introspection of a partition key that spans several columns.
Source code in pg_partsmith/sync/protocols.py
get_partition_columns(table_name)
¶
Return a table's own partition key columns, in key order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
The key columns in order; empty when the table is not partitioned. |
Source code in pg_partsmith/sync/protocols.py
Bases: Protocol
Lock manager for coordinating partition operations.
This protocol defines the interface for acquiring and releasing locks to prevent concurrent partition operations on the same table. Implement this to use a different locking backend (e.g. Zookeeper).
Source code in pg_partsmith/sync/protocols.py
acquire_lock(table_name)
¶
Acquire lock for partition operations on a table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name to lock. |
required |
Returns:
| Type | Description |
|---|---|
AbstractContextManager[None]
|
Context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If unable to acquire lock. |
Source code in pg_partsmith/sync/protocols.py
PostgreSQL implementations¶
PostgreSQL implementation of partition repository.
Facade that delegates to specialized helper classes for improved maintenance and SRP.
Unlike the async implementation, ddl_timeout_seconds is enforced
server-side via PostgreSQL statement_timeout (per statement) rather
than client-side around the whole operation.
Source code in pg_partsmith/sync/repositories/repository.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
ddl_timezone
property
¶
Timezone applied via SET LOCAL TIME ZONE around boundary-sensitive DDL.
None means the session timezone is trusted as-is.
adopt_partition(table_name, partition_name)
¶
Mark a detached legacy table as owned by this library (orphan marker).
See :meth:PartitionRemover.adopt. Use once when migrating an existing
partitioner instead of enabling drop_allow_unmanaged.
Source code in pg_partsmith/sync/repositories/repository.py
attach_composite_partition(table_name, partition_name, from_value, to_value, *, key_arity)
¶
Attach a partition to a parent with a composite partition key.
See :meth:PartitionCreator.attach_composite_partition.
Source code in pg_partsmith/sync/repositories/repository.py
attach_subpartition(parent_name, child_name, bounds)
¶
Attach one subpartition to its parent.
See :meth:PartitionCreator.attach_subpartition.
Source code in pg_partsmith/sync/repositories/repository.py
create_branch(config, branch_name, from_value, to_value, spec)
¶
Create a detached time partition that is itself partitioned.
See :meth:PartitionCreator.create_branch. Its buckets are created
separately and the branch is attached last, so an interrupted run can
never leave a partially-covering branch reachable from the root.
Source code in pg_partsmith/sync/repositories/repository.py
create_subpartition_table(parent_name, child_name, spec)
¶
Create a detached table shaped like parent_name.
See :meth:PartitionCreator.create_subpartition_table.
Source code in pg_partsmith/sync/repositories/repository.py
Provider for PostgreSQL partition metadata.
Queries pg_catalog to retrieve information about table partitioning. Override any method to customise catalog queries for your schema setup.
Each method opens its own read-only connection from the engine pool so it is safe to call outside any existing transaction.
Source code in pg_partsmith/sync/metadata.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | |
__init__(engine, *, marker_prefix=None, boundary_codec=None, ddl_timezone=None)
¶
Initialize provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
Engine
|
SQLAlchemy engine. |
required |
marker_prefix
|
str | None
|
Optional COMMENT marker prefix for orphaned partitions. When None, the library default prefix is used. Pass the same value to both repository and metadata provider if you override it. |
None
|
ddl_timezone
|
str | None
|
Session timezone to read naive boundary literals in.
Pass whatever the repository writes partitions with: a
|
None
|
boundary_codec
|
RangeBoundaryCodec | None
|
Codec used to read boundary literals back into instants. Required only when the partition key is an encoded identifier rather than a timestamp; pass the same codec the period calculator was built with. |
None
|
Source code in pg_partsmith/sync/metadata.py
get_default_partition(table_name)
¶
Get DEFAULT partition for a table if it exists and is attached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
Returns:
| Type | Description |
|---|---|
PartitionInfo | None
|
PartitionInfo with is_default=True, or None if no default partition exists. |
Source code in pg_partsmith/sync/metadata.py
get_partition_boundaries(partition_name)
¶
Get partition boundaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str] | None
|
Tuple of (from_value, to_value) or None if not a range partition. |
Source code in pg_partsmith/sync/metadata.py
get_partition_column(table_name)
¶
Get partition column for a table.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table uses a composite (multi-column) partition key. Only single-column keys are supported by this library. |
Source code in pg_partsmith/sync/metadata.py
get_partition_columns(table_name)
¶
Return a table's own partition key columns, in key order.
Unlike :meth:get_partition_column, which predates composite keys and
refuses them, this reports the whole key. Key order is not column
order, so it comes from partattrs' own ordering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
The key columns in order; empty when the table is not partitioned. |
Raises:
| Type | Description |
|---|---|
InvalidPartitionConfigError
|
If any key position is an expression rather than a column, which this library cannot address. |
Source code in pg_partsmith/sync/metadata.py
get_partition_tree(table_name)
¶
Return the whole partition tree rooted at table_name.
Unlike :meth:list_partitions, which reports the direct children a
lifecycle acts on, this walks the hierarchy to the leaves — the shape
subpartition reconciliation needs to know which buckets exist. One
round-trip regardless of depth.
Detached partitions are absent by construction: a detached branch is no longer part of its parent's tree. Query it by name to inspect it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Root of the tree, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
PartitionNode | None
|
The root node with its descendants, or None when |
PartitionNode | None
|
not partitioned and is not itself a partition. |
Source code in pg_partsmith/sync/metadata.py
get_partition_type(table_name)
¶
Get partition type for a table.
Source code in pg_partsmith/sync/metadata.py
get_unique_constraint_columns(table_name)
¶
Return the column tuples of every UNIQUE / PRIMARY KEY constraint.
PostgreSQL requires such a constraint on a partitioned table to contain all of its partition-key columns. Reading them lets a subpartitioning config be refused with an explanation before any DDL is attempted, instead of failing halfway through a maintenance run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table to inspect, schema-qualified. |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
One tuple of column names per constraint; empty when the table has |
...
|
no unique constraints at all. |
Source code in pg_partsmith/sync/metadata.py
is_partition_attached(table_name, partition_name)
¶
Check if a partition is currently attached to its parent via pg_inherits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the partition is attached. |
Source code in pg_partsmith/sync/metadata.py
is_partition_closed(partition_name, *, settle_seconds=0)
¶
True when the partition's upper bound (+ settle buffer) has passed.
now() is evaluated on the server rather than on the client, so the
answer tolerates app-clock skew. Useful for export/archive pipelines
that must only finalize partitions which can no longer receive
in-range rows.
Works for a subpartitioned branch exactly as for a plain leaf: what is read is the branch's own RANGE bound in the root table, and its whole subtree closes with it.
A naive bound -- which is what a timestamp or date key produces
-- is resolved under this provider's ddl_timezone. Configure it with
the same value the repository writes partitions with, or the two
disagree about when the bound falls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Attached partition table name. |
required |
settle_seconds
|
int
|
Extra buffer after the upper bound for late writers still holding open transactions. |
0
|
Returns:
| Type | Description |
|---|---|
bool
|
True when |
bool
|
DEFAULT partition, non-RANGE partitions, unbounded upper bounds |
bool
|
(MAXVALUE / infinity), detached tables, unresolvable names, and |
bool
|
boundaries that carry no instant this provider can read. |
Source code in pg_partsmith/sync/metadata.py
list_partitions(table_name)
¶
List all partitions for a table, including orphaned detached ones.
Orphaned partitions are detached-but-not-dropped tables previously
detached by this library. They are detected by a COMMENT marker set on
successful detach and returned with is_attached=False and None
boundaries.
Partition names are always schema-qualified with the child's catalog
schema — a partition may live in a different schema than its parent,
and a bare name could resolve to an unrelated table via search_path.
Source code in pg_partsmith/sync/metadata.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
partition_exists(partition_name)
¶
Check if a partition table exists in pg_class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
partition_name
|
str
|
Partition table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the table exists as a regular or partitioned table |
bool
|
(a partition may itself be subpartitioned). |
Source code in pg_partsmith/sync/metadata.py
Lock managers¶
Lock manager using PostgreSQL advisory locks.
Holds the advisory lock on a dedicated AUTOCOMMIT connection from the engine pool. This guarantees the lock survives any number of commits or rollbacks on the caller's session, which is required when the caller needs to commit DDL (e.g. ATTACH PARTITION) before running DETACH PARTITION CONCURRENTLY.
Override _compute_lock_id to customise the lock ID derivation.
Source code in pg_partsmith/sync/lock/postgres.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | |
__init__(engine, prefix=DEFAULT_LOCK_PREFIX, acquire_min_interval_seconds=0.0)
¶
Initialize lock manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
Engine
|
SQLAlchemy engine used to open a dedicated connection for the advisory lock. |
required |
prefix
|
str
|
Prefix for lock key generation. |
DEFAULT_LOCK_PREFIX
|
acquire_min_interval_seconds
|
float
|
Minimum seconds between acquire attempts per table (rate limiting). 0 disables. |
0.0
|
Source code in pg_partsmith/sync/lock/postgres.py
acquire_lock(table_name)
¶
Acquire advisory lock for a table.
Opens a dedicated AUTOCOMMIT connection from the engine pool and acquires a session-level advisory lock on it. The lock is released when the context manager exits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name to lock. |
required |
Returns:
| Type | Description |
|---|---|
AbstractContextManager[None]
|
Context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the lock cannot be acquired. |
Source code in pg_partsmith/sync/lock/postgres.py
is_locked(table_name)
¶
Check if lock is held by any session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the advisory lock for the given table is currently held. |
Source code in pg_partsmith/sync/lock/postgres.py
Lock manager using Redis for distributed coordination.
Uses SET NX EX to acquire the lock and a background renewal thread to
extend the TTL while the lock is held, preventing expiry during long DDL
operations (e.g. DETACH PARTITION CONCURRENTLY). The lock is released
atomically via a Lua script that checks the ownership token, so it is safe
even if Redis restarts during the renewal window.
The renewal interval is ttl_seconds // 3 (with random jitter to avoid
thundering herds). If renewal fails — e.g. Redis is unreachable or another
holder takes over — the watchdog logs a warning and stops renewing. Unlike
the async version it cannot cancel the maintenance run, so the TTL becomes
the upper bound on how long a stale holder can keep working.
For production use you may want to subclass and override acquire_lock
to use Redlock or another algorithm with stronger guarantees.
Raises:
| Type | Description |
|---|---|
ImportError
|
If the |
Source code in pg_partsmith/sync/lock/redis.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | |
__init__(redis_client, prefix=_DEFAULT_REDIS_LOCK_PREFIX, ttl_seconds=300, acquire_min_interval_seconds=0.0)
¶
Initialize lock manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redis_client
|
RedisClientProtocol
|
Redis client instance. |
required |
prefix
|
str
|
Prefix for Redis keys. |
_DEFAULT_REDIS_LOCK_PREFIX
|
ttl_seconds
|
int
|
Lock time-to-live in seconds. The lock is automatically
renewed every |
300
|
acquire_min_interval_seconds
|
float
|
Minimum seconds between acquire attempts per table (rate limiting). 0 disables. |
0.0
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
ValueError
|
If |
Source code in pg_partsmith/sync/lock/redis.py
acquire_lock(table_name)
¶
Acquire Redis lock with automatic TTL renewal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Table name. |
required |
Returns:
| Type | Description |
|---|---|
AbstractContextManager[None]
|
Context manager for the lock. |
Raises:
| Type | Description |
|---|---|
LockAcquisitionError
|
If the lock is already held. |
Source code in pg_partsmith/sync/lock/redis.py
is_locked(table_name)
¶
Return True if the Redis lock for table_name is currently held.
Hooks¶
No-op base implementation of partition lifecycle hooks.
Subclass and override only the methods you need. All methods are no-ops by default so you can selectively add behaviour without implementing every step.
Source code in pg_partsmith/sync/hooks.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
after_create(config, partition)
¶
Called after a partition has been created (and optionally attached).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition
|
PartitionInfo
|
Info about the newly created partition. |
required |
Source code in pg_partsmith/sync/hooks.py
after_detach(table_name, partition_name)
¶
after_drop(table_name, partition_name)
¶
before_create(config, partition_name, from_value, to_value)
¶
Called before a partition is created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TablePartitionConfig
|
Table partition configuration. |
required |
partition_name
|
str
|
Name the new partition will be given. |
required |
from_value
|
str
|
Start boundary value. |
required |
to_value
|
str
|
End boundary value. |
required |
Source code in pg_partsmith/sync/hooks.py
before_detach(table_name, partition)
¶
Called before a partition is detached from its parent table.
This is a good place to export or archive data while the partition is still accessible via the parent table's indexes and constraints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition
|
PartitionInfo
|
Info about the partition being detached. |
required |
Source code in pg_partsmith/sync/hooks.py
before_drop(table_name, partition_name)
¶
Called before a partition table is dropped.
This is the last chance to read or export data from the partition before it is permanently destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Parent table name. |
required |
partition_name
|
str
|
Name of the partition about to be dropped. |
required |
Source code in pg_partsmith/sync/hooks.py
pg_partsmith.settings¶
Env-driven configuration via pydantic-settings.
Requires the pydantic-settings extra: pip install pg-partsmith[pydantic-settings].
Settings¶
Bases: BaseSettings
Env-loadable base class that maps 1-to-1 with :class:~pg_partsmith.TablePartitionConfig.
Subclass it, set model_config with your env prefix, then call
:meth:to_config to get a ready-to-use TablePartitionConfig.
All fields correspond directly to TablePartitionConfig arguments.
PartitionType, PartitionStrategy, and PartitionGranularity
are StrEnum values — env vars accept their lowercase string forms
(e.g. GRANULARITY=month).
Example::
class OutboxSettings(PartitionTableSettings):
model_config = SettingsConfigDict(env_prefix="OUTBOX_")
# Reads: OUTBOX_TABLE_NAME, OUTBOX_PARTITION_TYPE, OUTBOX_GRANULARITY, …
settings = OutboxSettings()
config = settings.to_config()
calculator = settings.get_period_calculator()
Source code in pg_partsmith/settings.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
get_period_calculator(tz=UTC)
¶
Return the period calculator matching :attr:granularity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
tzinfo
|
Timezone the calculator works in ( |
UTC
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pg_partsmith/settings.py
to_config()
¶
Build a :class:~pg_partsmith.TablePartitionConfig from these settings.