Skip to content

Commit 0e7fd1a

Browse files
committed
Fully document table partitioning
1 parent 69470a0 commit 0e7fd1a

File tree

4 files changed

+195
-105
lines changed

4 files changed

+195
-105
lines changed

docs/source/api_reference.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ API Reference
3737
.. autoclass:: ConditionalUniqueIndex
3838
.. autoclass:: CaseInsensitiveUniqueIndex
3939

40+
.. automodule:: psqlextra.partitioning
41+
:members:
42+
43+
.. automodule:: psqlextra.backend.migrations.operations
44+
:members:
45+
4046
.. automodule:: psqlextra.types
4147
:members:
4248
:undoc-members:

docs/source/table_partitioning.rst

Lines changed: 187 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,130 @@ This will generate a migration that creates the partitioned table with a default
8080
Do not use the standard ``python manage.py makemigrations`` command for partitioned models. Django will issue a standard :class:`~django:django.db.migrations.operations.CreateModel` operation. Doing this will not create a partitioned table and all subsequent operations will fail.
8181

8282

83-
Adding/removing partitions manually
84-
-----------------------------------
83+
Automatically managing partitions
84+
---------------------------------
8585

86-
Postgres does not have support for automatically creating new partitions as needed. Therefore, one must manually add new partitions. Depending on the partitioning method you have chosen, the partition has to be created differently.
86+
The ``python manage.py pgpartition`` command can help you automatically create new partitions ahead of time and delete old ones for time-based partitioning.
8787

88-
Partitions are tables. Each partition must be given a unique name. :class:`~psqlextra.models.PostgresPartitionedModel` does not require you to create a model for each partition because you are not supposed to query partitions directly.
88+
You can run this command manually as needed, schedule to run it periodically or run it every time you release a new version of your app.
8989

90+
.. warning::
91+
92+
We DO NOT recommend that you set up this command to automatically delete partitions without manual review.
93+
94+
Specify ``--skip-delete`` to not delete partitions automatically. Run the command manually periodically without the ``--yes`` flag to review partitions to be deleted.
95+
96+
97+
Command-line options
98+
********************
99+
100+
==================== ============= ================ ==================================================================================================== === === === === === ===
101+
Long flag Short flag Default Description
102+
==================== ============= ================ ==================================================================================================== === === === === === ===
103+
``--yes`` ``-y`` ``False`` Specifies yes to all questions. You will NOT be asked for confirmation before partition deletion.
104+
``--using`` ``-u`` ``'default'`` Name of the database connection to use.
105+
``--skip-create`` ``False`` Whether to skip creating partitions.
106+
``--skip-delete`` ``False`` Whether to skip deleting partitions.
107+
108+
==================== ============= ================ ==================================================================================================== === === === === === ===
109+
110+
111+
Configuration
112+
*************
113+
114+
In order to use the command, you have to declare an instance of :class:`psqlextra.partitioning.PostgresPartitioningManager` and set ``PSQLEXTRA_PARTITIONING_MANAGER`` to a string with the import path to your instance of :class:`psqlextra.partitioning.PostgresPartitioningManager`.
115+
116+
For example:
117+
118+
.. code-block:: python
119+
120+
# myapp/partitioning.py
121+
from psqlextra.partitioning import PostgresPartitioningManager
122+
123+
manager = PostgresPartitioningManager(...)
124+
125+
# myapp/settings.py
126+
PSQLEXTRA_PARTITIONING_MANAGER = 'myapp.partitioning.manager'
127+
128+
129+
Time-based partitioning
130+
~~~~~~~~~~~~~~~~~~~~~~~
131+
132+
.. code-block:: python
133+
134+
from dateutil.relativedelta import relativedelta
135+
136+
from psqlextra.partitioning import (
137+
PostgresPartitioningManager,
138+
PostgresCurrentTimePartitioningStrategy,
139+
PostgresTimePartitionSize,
140+
partition_by_current_time,
141+
)
142+
143+
manager = PostgresPartitioningManager([
144+
# 3 partitions ahead, each partition is one month
145+
# delete partitions older than 6 months
146+
# partitions will be named `[table_name]_[year]_[3-letter month name]`.
147+
PostgresPartitioningConfig(
148+
model=MyPartitionedModel,
149+
strategy=PostgresCurrentTimePartitioningStrategy(
150+
size=PostgresTimePartitionSize(months=1),
151+
count=3,
152+
max_age=relativedelta(months=6),
153+
),
154+
),
155+
# 6 partitions ahead, each partition is two weeks
156+
# delete partitions older than 8 months
157+
# partitions will be named `[table_name]_[year]_week_[week number]`.
158+
PostgresPartitioningConfig(
159+
model=MyPartitionedModel,
160+
strategy=PostgresCurrentTimePartitioningStrategy(
161+
size=PostgresTimePartitionSize(weeks=2),
162+
count=6,
163+
max_age=relativedelta(months=8),
164+
),
165+
),
166+
# 12 partitions ahead, each partition is 5 days
167+
# old partitions are never deleted, `max_age` is not set
168+
# partitions will be named `[table_name]_[year]_[month]_[month day number]`.
169+
PostgresPartitioningConfig(
170+
model=MyPartitionedModel,
171+
strategy=PostgresCurrentTimePartitioningStrategy(
172+
size=PostgresTimePartitionSize(wdyas=5),
173+
count=12,
174+
),
175+
),
176+
])
177+
178+
179+
Changing a time partitioning strategy
180+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
181+
182+
When switching partitioning strategies, you might encounter the problem that partitions for part of a particular range already exist.
183+
184+
In order to combat this, you can use the :class:`psqlextra.partitioning.PostgresTimePartitioningStrategy` and specify the `start_datetime` parameter. As a result, no partitions will be created before the given date/time.
185+
186+
187+
Custom strategy
188+
~~~~~~~~~~~~~~~
189+
190+
You can create a custom partitioning strategy by implementing the :class:`psqlextra.partitioning.PostgresPartitioningStrategy` interface.
191+
192+
You can look at :class:`psqlextra.partitioning.PostgresCurrentTimePartitioningStrategy` as an example.
193+
194+
195+
Manually managing partitions
196+
----------------------------
197+
198+
If you are using list or has partitioning, you most likely have a fixed amount of partitions that can be created up front using migrations or using the schema editor.
199+
200+
Using migration operations
201+
**************************
90202

91203
Adding a range partition
92204
~~~~~~~~~~~~~~~~~~~~~~~~
93205

94-
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddRangePartition` operation to add a new range partition. Only use this operation when your partitioned model uses the :attr:`psqlextra.types.PostgresPartitioningMethod.RANGE`.
206+
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddRangePartition` operation to add a new range partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.RANGE`.
95207

96208
.. code-block:: python
97209
@@ -113,7 +225,7 @@ Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddRangePartiti
113225
Adding a list partition
114226
~~~~~~~~~~~~~~~~~~~~~~~
115227

116-
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddListPartition` operation to add a new list partition. Only use this operation when your partitioned model uses the :attr:`psqlextra.types.PostgresPartitioningMethod.LIST`.
228+
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddListPartition` operation to add a new list partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.LIST`.
117229

118230
.. code-block:: python
119231
@@ -131,12 +243,36 @@ Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddListPartitio
131243
]
132244
133245
246+
Adding a hash partition
247+
~~~~~~~~~~~~~~~~~~~~~~~
248+
249+
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddHashPartition` operation to add a new list partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.HASH`.
250+
251+
.. code-block:: python
252+
253+
from django.db import migrations, models
254+
255+
from psqlextra.backend.migrations.operations import PostgresAddHashPartition
256+
257+
class Migration(migrations.Migration):
258+
operations = [
259+
PostgresAddHashPartition(
260+
model_name="mypartitionedmodel",
261+
name="pt1",
262+
modulus=3,
263+
remainder=1,
264+
),
265+
]
266+
267+
134268
Adding a default partition
135269
~~~~~~~~~~~~~~~~~~~~~~~~~~
136270

137-
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddDefaultPartition` operation to add a new default partition. A default partition is the partition where records get saved that couldn't fit in any other partition.
271+
Use the :class:`~psqlextra.backend.migrations.operations.PostgresAddDefaultPartition` operation to add a new list partition.
272+
273+
Note that you can only have one default partition per partitioned table/model. An error will be thrown if you try to create a second default partition.
138274

139-
Note that you can only have one default partition per partitioned table/model.
275+
If you used ``python manage.py pgmakemigrations`` to generate a migration for your newly created partitioned model, you do not need this operation. This operation is added automatically when you create a new partitioned model.
140276

141277
.. code-block:: python
142278
@@ -158,6 +294,12 @@ Deleting a default partition
158294

159295
Use the :class:`~psqlextra.backend.migrations.operations.PostgresDeleteDefaultPartition` operation to delete an existing default partition.
160296

297+
298+
.. warning::
299+
300+
Deleting the default partition and leaving your model without a default partition can be dangerous. Rows that do not fit in any other partition will fail to be inserted.
301+
302+
161303
.. code-block:: python
162304
163305
from django.db import migrations, models
@@ -176,7 +318,7 @@ Use the :class:`~psqlextra.backend.migrations.operations.PostgresDeleteDefaultPa
176318
Deleting a range partition
177319
~~~~~~~~~~~~~~~~~~~~~~~~~~
178320

179-
Use the :class:`psqlextra.backend.migrations.operations.PostgresDeleteRangePartition` operation to delete an existing range partition.
321+
Use the :class:`psqlextra.backend.migrations.operations.PostgresDeleteRangePartition` operation to delete an existing range partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.RANGE`.
180322

181323
.. code-block:: python
182324
@@ -196,7 +338,7 @@ Use the :class:`psqlextra.backend.migrations.operations.PostgresDeleteRangeParti
196338
Deleting a list partition
197339
~~~~~~~~~~~~~~~~~~~~~~~~~
198340

199-
Use the :class:`~psqlextra.backend.migrations.operations.PostgresDeleteListPartition` operation to delete an existing list partition.
341+
Use the :class:`psqlextra.backend.migrations.operations.PostgresDeleteListPartition` operation to delete an existing range partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.LIST`.
200342

201343
.. code-block:: python
202344
@@ -213,6 +355,26 @@ Use the :class:`~psqlextra.backend.migrations.operations.PostgresDeleteListParti
213355
]
214356
215357
358+
Deleting a hash partition
359+
~~~~~~~~~~~~~~~~~~~~~~~~~
360+
361+
Use the :class:`psqlextra.backend.migrations.operations.PostgresDeleteHashPartition` operation to delete an existing range partition. Only use this operation when your partitioned model uses :attr:`psqlextra.types.PostgresPartitioningMethod.HASH`.
362+
363+
.. code-block:: python
364+
365+
from django.db import migrations, models
366+
367+
from psqlextra.backend.migrations.operations import PostgresDeleteHashPartition
368+
369+
class Migration(migrations.Migration):
370+
operations = [
371+
PostgresDeleteHashPartition(
372+
model_name="mypartitionedmodel",
373+
name="pt1",
374+
),
375+
]
376+
377+
216378
Using the schema editor
217379
***********************
218380

@@ -248,120 +410,42 @@ Adding a list partition
248410
)
249411
250412
251-
Adding a default partition
252-
~~~~~~~~~~~~~~~~~~~~~~~~~~
413+
Adding a hash partition
414+
~~~~~~~~~~~~~~~~~~~~~~~
253415

254416
.. code-block:: python
255417
256418
from django.db import connection
257419
258-
connection.schema_editor().add_default_partition(
420+
connection.schema_editor().add_hash_partition(
259421
model=MyPartitionedModel,
260-
name="default",
422+
name="pt1",
423+
modulus=3,
424+
remainder=1,
261425
)
262426
263427
264-
Deleting a partition
265-
~~~~~~~~~~~~~~~~~~~~
428+
Adding a default partition
429+
~~~~~~~~~~~~~~~~~~~~~~~~~~
266430

267431
.. code-block:: python
268432
269433
from django.db import connection
270434
271-
connection.schema_editor().delete_partition(
435+
connection.schema_editor().add_default_partition(
272436
model=MyPartitionedModel,
273437
name="default",
274438
)
275439
276440
277-
Adding/removing partitions automatically
278-
----------------------------------------
279-
280-
:class:`psqlextra.partitioning.PostgresPartitioningManager` an experimental helper class that can be called periodically to automatically create new partitions if you're using range partitioning.
281-
282-
.. note::
283-
284-
There is currently no scheduler or command to automatically create new partitions. You'll have to run this function in your own cron jobs.
285-
286-
The auto partitioner supports automatically creating yearly, monthly, weekly or daily partitions. Use the ``count`` parameter to configure how many partitions it should create ahead.
287-
288-
289-
Partitioning strategies
290-
***********************
291-
292-
293-
Time-based partitioning
294-
~~~~~~~~~~~~~~~~~~~~~~~
441+
Deleting a partition
442+
~~~~~~~~~~~~~~~~~~~~
295443

296444
.. code-block:: python
297445
298-
from dateutil.relativedelta import relativedelta
299-
300-
from psqlextra.partitioning import (
301-
PostgresPartitioningManager,
302-
PostgresCurrentTimePartitioningStrategy,
303-
PostgresTimePartitionSize,
304-
partition_by_current_time,
305-
)
306-
307-
manager = PostgresPartitioningManager([
308-
# 3 partitions ahead, each partition is one month
309-
# delete partitions older than 6 months
310-
# partitions will be named `[table_name]_[year]_[3-letter month name]`.
311-
PostgresPartitioningConfig(
312-
model=MyPartitionedModel,
313-
strategy=PostgresCurrentTimePartitioningStrategy(
314-
size=PostgresTimePartitionSize(months=1),
315-
count=3,
316-
max_age=relativedelta(months=6),
317-
),
318-
),
319-
# 6 partitions ahead, each partition is two weeks
320-
# delete partitions older than 8 months
321-
# partitions will be named `[table_name]_[year]_week_[week number]`.
322-
PostgresPartitioningConfig(
323-
model=MyPartitionedModel,
324-
strategy=PostgresCurrentTimePartitioningStrategy(
325-
size=PostgresTimePartitionSize(weeks=2),
326-
count=6,
327-
max_age=relativedelta(months=8),
328-
),
329-
),
330-
# 12 partitions ahead, each partition is 5 days
331-
# old partitions are never deleted, `max_age` is not set
332-
# partitions will be named `[table_name]_[year]_[month]_[month day number]`.
333-
PostgresPartitioningConfig(
334-
model=MyPartitionedModel,
335-
strategy=PostgresCurrentTimePartitioningStrategy(
336-
size=PostgresTimePartitionSize(wdyas=5),
337-
count=12,
338-
),
339-
),
340-
])
446+
from django.db import connection
341447
342-
# these are the default arguments
343-
partioning_plan = manager.plan(
344-
skip_create=False,
345-
skip_delete=False,
346-
using='default'
448+
connection.schema_editor().delete_partition(
449+
model=MyPartitionedModel,
450+
name="default",
347451
)
348-
349-
# prints a list of partitions to be created/deleted
350-
partitioning_plan.print()
351-
352-
# apply the plan
353-
partitioning_plan.apply(using='default');
354-
355-
356-
Custom strategy
357-
~~~~~~~~~~~~~~~
358-
359-
You can create a custom partitioning strategy by implementing the :class:`psqlextra.partitioning.PostgresPartitioningStrategy` interface.
360-
361-
You can look at :class:`psqlextra.partitioning.PostgresCurrentTimePartitioningStrategy` as an example.
362-
363-
364-
Switching partitioning strategies
365-
*********************************
366-
367-
When switching partitioning strategies, you might encounter the problem that partitions for part of a particular range already exist. In order to combat this, you can use the :class:`psqlextra.partitioning.PostgresTimePartitioningStrategy` and specify the `start_datetime` parameter. As a result, no partitions will be created before the given date/time.

psqlextra/management/commands/pgpartition.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
from typing import Optional
32

43
from django.conf import settings

0 commit comments

Comments
 (0)