Automated Partition Management

This document describes the automated partition management policies and usage in YMatrix.

In production environments, data is often partitioned by time. YMatrix provides several user-defined functions (UDFs) for managing partitions, allowing administrators to manually maintain partitions based on their requirements—for example, creating one partition per day or every 8 hours.
However, time is an infinite dimension. When creating partitioned tables, administrators typically pre-create partitions only for a limited future period and then periodically add new partitions. This increases operational overhead.

Automated partition management eliminates the need for manual maintenance by automating partition lifecycle operations, thereby reducing administrative burden. It supports the following features:

  1. Automatic creation and deletion of partitions
  2. Automatic splitting of default partitions
  3. Batch creation of partitions
  4. Forced retention of specific historical partitions
  5. Customization of operation time windows for automated tasks

The automated partition management functionality is included in the matrixts extension. Therefore, you must first create the matrixts extension:

=# CREATE EXTENSION matrixts;

Use the set_policy() function to assign a policy to a target table and enable automatic partition maintenance.

Currently, three policies are supported:

  1. auto_partitioning
  2. auto_splitting
  3. auto_partitioning_ex

Note!
Starting from version 5.0, YMatrix introduces the auto_partitioning_ex policy. Use this policy instead of auto_partitioning to take advantage of advanced features such as batch partition creation, forced retention of specific historical partitions, and customizable operation time windows.

1 Partition Policies

1.1 auto_partitioning

The auto_partitioning policy enables automated management of regular partitioned tables and provides two automation capabilities:

  1. Creation of new partitions
  2. Cleanup of expired partitions

Example: Create a test partitioned table named metrics:

=# CREATE TABLE metrics(
   time timestamp with time zone,
   tag_id int,
   read float,
   write float
   )
   USING MARS3
   DISTRIBUTED BY (tag_id)
   ORDER BY (time,tag_id)
   PARTITION BY RANGE (time);

Set the auto_partitioning auto-partitioning policy on the metrics table:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_partitioning'
);

If schema qualification is required, pass the parameter as 'schema_name.metrics'.

After setting the policy, use list_policy() to view it:

=# SELECT * FROM list_policy('metrics'::regclass);
 reloid | relname | class_id |    class_name     |   action    | seq | disabled |               check_func               |      check_args      |                    act_func                    |       act_args        | versi
on
--------+---------+----------+-------------------+-------------+-----+----------+----------------------------------------+----------------------+------------------------------------------------+-----------------------+------
---
  23361 | metrics |        2 | auto_partitioning | retention   |   1 | t        | matrixts_internal.apm_generic_expired  | {"after": "1 year"}  | matrixts_internal.apm_generic_drop_partition   | {}                    | 1.0
  23361 | metrics |        2 | auto_partitioning | auto_create |   2 | f        | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_partition | {"period": "8 hours"} | 1.0
(2 rows)

As shown, the auto_partitioning policy includes two actions:

  1. retention: Delete expired partitions
  2. auto_create: Create new partitions

The retention action uses the check argument {"after": "1 year"}, meaning that any partition older than one year will be deleted.
The auto_create action checks whether a new partition should be created using {"before": "3 days"}—that is, three days in advance—and creates partitions with a time span of {"period": "8 hours"}. These are the default settings for the auto_partitioning policy.

To adjust these parameters, call set_policy_action().

Example: Change retention to delete partitions after 3 months; modify auto_create to look ahead 2 days and create daily partitions:

=# SELECT set_policy_action(
    'metrics'::regclass,
    'retention',
    '3 months'
);

=# SELECT set_policy_action(
    'metrics'::regclass,
    'auto_create',
    '{
        "before": "2 days",
        "period": "1 day"
    }'
);

Note!
For actions with a single parameter, provide the target value directly as a string (e.g., '3 months'). For actions with multiple parameters, use a JSON object to specify all arguments (e.g., '{"before": "2 days", "period": "1 day"}').

1.2 auto_splitting

The auto_splitting policy applies to partitioned tables that include a default partition. It automatically splits the default partition based on time.

Example: Create a partitioned table and define a default partition:

=# CREATE TABLE metrics(
   time timestamp with time zone,
   tag_id int,
   read float,
   write float
   )
   USING MARS3
   DISTRIBUTED BY (tag_id)
   ORDER BY (time,tag_id)
   PARTITION BY RANGE (time);

=# CREATE TABLE metrics_others PARTITION OF metrics DEFAULT;

Apply the auto_splitting policy:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_splitting'
);

View the policy:

=# SELECT * FROM list_policy('metrics'::regclass);
 reloid | relname | class_id |   class_name   |   action   | seq | disabled |                check_func                |        check_args        |                       act_func                        |       act_args        | version
--------+---------+----------+----------------+------------+-----+----------+------------------------------------------+--------------------------+-------------------------------------------------------+-----------------------+---------
  17798 | metrics |        1 | auto_splitting | retention  |   1 | t        | matrixts_internal.apm_generic_expired    | {"after": "1 year"}      | matrixts_internal.apm_generic_drop_partition          | {}                    | 1.0
  17798 | metrics |        1 | auto_splitting | auto_split |   2 | f        | matrixts_internal.apm_generic_older_than | {"age": "1 month 2days"} | matrixts_internal.apm_generic_split_default_partition | {"period": "1 month"} | 1.0
(2 rows)

The auto_splitting policy includes two actions: retention (delete expired partitions) and auto_split (automatically split the default partition).

Here, {"age": "1 month 2days"} means the split is triggered when the oldest data in the default partition is older than 1 month and 2 days.
{"period": "1 month"} indicates that each resulting partition covers a 1-month interval.

1.3 auto_partitioning_ex

The auto_partitioning_ex policy extends the functionality of auto_partitioning, offering advanced partition management features:

  1. Batch creation of partitions
  2. Forced retention of specific historical partitions
  3. Customizable time windows for automated operations

These features are detailed in the examples below.

Create a partitioned table and define a special partition:

=# CREATE TABLE metrics(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
    )
    USING MARS3
    DISTRIBUTED BY (tag_id)
    ORDER BY (time,tag_id)
    PARTITION BY RANGE (time);

=# CREATE TABLE metrics_invalid_apmtrap PARTITION OF metrics FOR VALUES FROM ('-infinity') TO ('2023-01-01');

Set the auto_partitioning_ex policy:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_partitioning_ex'
);

View the policy:

=# SELECT * FROM list_policy('metrics'::regclass);
reloid | relname | class_id |      class_name      |   action    | seq | disabled |                check_func                 |                                                               check_args
                   |                    act_func                    |       act_args        | version
--------+---------+----------+----------------------+-------------+-----+----------+-------------------------------------------+---------------------------------------------------------------------------------------------------------------------
-------------------+------------------------------------------------+-----------------------+---------
  34917 | metrics |        4 | auto_partitioning_ex | retention   |   1 | t        | matrixts_internal.apm_generic_expired     | {"after": "1 year"}
                   | matrixts_internal.apm_generic_drop_partition   | {}                    | 1.0
  34917 | metrics |        4 | auto_partitioning_ex | auto_create |   2 | f        | matrixts_internal.apm_generic_incoming_ex | {"before": "3 days", "period": "8 hours", "end_time": "00:00:00", "batch_size": "0", "start_time": "00:00:00", "stor
age_type": "heap"} | matrixts_internal.apm_generic_append_partition | {"period": "8 hours"} | 1.0
(2 rows)

1. Batch Creation of Partitions

From the output, the auto_create action under auto_partitioning_ex has the parameter "before": "3 days" and "period": "8 hours". This means the automated system will ensure that all partitions for the next 3 days are pre-created, each covering an 8-hour interval. For example, if the current time is March 1, 00:01, the system triggers partition creation and ensures all partitions up to March 4, 00:00 are ready. At March 4, 00:00, the system begins creating the next 8-hour partition (March 4, 00:00–08:00). With this design, the auto_partitioning_ex policy guarantees that partitions for the next 3 days are always prepared in advance.

However, other concurrent tasks may hold locks on the table, causing lock contention when the automated system attempts to create partitions. To reduce the frequency of such contention, the auto_partitioning_ex policy introduces the batch_size option.

Use the following command to set it:

=# SELECT public.set_policy_action('metrics'::regclass, 'auto_create', '{ "batch_size": "4" }');

This means that whenever a new partition needs to be created, the system will create 4 additional future partitions at once. So, when creating the March 4, 00:00–08:00 partition on March 1, 00:01, it will also create the next four 8-hour partitions: March 4, 08:00–16:00; March 4, 16:00–24:00; March 5, 00:00–08:00; and March 5, 08:00–16:00.
This avoids triggering partition creation again between March 1 and March 4, thus reducing lock contention.

  • The default value of batch_size is 0, meaning no batching occurs—the behavior is identical to auto_partitioning.
  • The maximum value of batch_size is 7.

Note!
While batch creation reduces lock contention frequency (e.g., from daily to every few days), it cannot eliminate lock conflicts entirely when the automated task executes DDL statements. This limitation stems from fundamental database concurrency mechanisms.
Therefore, adequate monitoring and alerting for queries, locks, and automated partition management execution remain essential.

2. Forced Retention of Specific Historical Partitions

As shown above, we created a partition named metrics_invalid_apmtrap. When the retention feature (automatic partition deletion) is enabled under auto_partitioning_ex, any partition whose name contains the string apmtrap (such as metrics_invalid_apmtrap) will not be dropped, even if it meets the expiration condition. This achieves permanent retention of designated historical partitions.

This feature is designed to implement a "trap" partition that captures out-of-range data (e.g., timestamps like 1970-01-01). The trap partition absorbs invalid data that would otherwise go into the default partition, preventing rapid growth of the default partition. Additionally, since the trap partition does not participate in queries or DDL operations, it helps preserve query performance.

3. Customizing Automated Partition Operation Time Windows

From the earlier list_policy() result, observe the parameters start_time and end_time in the check_args field. These parameters control the time window during which automated partition management tasks can run, helping avoid partition creation during peak business hours.

For example:

  • "start_time": "01:00:00", "end_time": "04:00:00" allows operations between 1:00 AM and 4:00 AM daily.

  • "start_time": "22:00:00", "end_time": "03:00:00" allows operations from 10:00 PM to 3:00 AM the next day.

  • The default value is "00:00:00" for both fields, indicating no time restriction—operations are allowed at any time.

Example:

SELECT public.set_policy_action('table_name'::regclass, 'auto_create', '{ "start_time": "00:00:00", "end_time": "04:00:00" }');

Note!
The time window must be at least 1 hour long. The default check interval for automated partition management is 1 hour. A window shorter than 1 hour may miss the trigger opportunity.
Reaching the end_time does not immediately terminate ongoing operations. Any triggered task continues until completion. The end_time only prevents new checks from being initiated after that point—it does not forcibly stop running tasks. Therefore, when setting the end_time, allow sufficient buffer time before the next business peak.

2 Managing Action States

From the list_policy() output, note the disabled column. This column controls whether a policy action is active. By default, regardless of the policy type, the retention action is disabled.

To enable an action, use enable_policy_action():

=# SELECT enable_policy_action('metrics'::regclass, 'retention');

To disable an action, use disable_policy_action():

=# SELECT disable_policy_action('metrics', 'auto_split');

3 Dropping a Policy

Use drop_policy() to remove a policy from a partitioned table:

=# SELECT drop_policy('metrics'::regclass);

Note!
Each partitioned table can have only one partition policy. Therefore, only the table name is required to drop the policy.

4 Log Monitoring

--- SPLIT ---

4.1 apm_operation_log

The apm_operation_log table records every user operation performed when configuring APM. Example:

=# SELECT username = CURRENT_USER, relname, class_id, class_name, action, operation, mod_field, old_val, new_val
FROM matrixts_internal.apm_operation_log ORDER BY ts;
 ?column? |      relname       | class_id | class_name |   action   | operation | mod_field  |        old_val         |        new_val         
----------+--------------------+----------+------------+------------+-----------+------------+------------------------+------------------------
 t        | apm_test.split_set |          |            | test_split | add       | check_args |                        | {"age": "1 month"}
 t        | apm_test.split_set |          |            | test_split | add       | act_args   |                        | {"period": "2 weeks"}
 t        | apm_test.split_set |          |            | test_split | mod       | act_args   | {"period": "2 weeks"}  | {"period": "12 hours"}
 t        | apm_test.split_set |          |            | test_split | mod       | check_args | {"age": "1 month"}     | {"age": "15 days"}
 t        | apm_test.split_set |          |            | test_split | mod       | check_args | {"age": "15 days"}     | {"age": "5 days"}
 t        | apm_test.split_set |          |            | test_split | mod       | act_args   | {"period": "12 hours"} | {"period": "2 days"}
 t        | apm_test.split_set |          |            | test_split | drop      |            |                        | 
 t        | apm_test.split_set |          |            | test_split | add       | check_args |                        | {"age": "10 days"}
 t        | apm_test.split_set |          |            | test_split | add       | act_args   |                        | {"period": "1 hour"}
(9 rows)

4.2 apm_action_log

The apm_action_log table records each automatic partitioning operation executed by the APM background process. Example:

=# (select * from matrixts_internal.apm_action_log order by ts limit 6) union all (select * from matrixts_internal.apm_action_log order by ts desc limit 6) order by 1;
             ts             |  pid  | reloid |         relname          |      nspname       | class_id |    class_name     |   action    |               check_func               |      check_args      |                    act_func
      |      act_args       |  state  |      message      | details | addopt |  client
----------------------------+-------+--------+--------------------------+--------------------+----------+-------------------+-------------+----------------------------------------+----------------------+------------------------------------------
------+---------------------+---------+-------------------+---------+--------+----------
 2022-11-18 15:48:45.963264 | 15776 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-18 15:48:46.165418 | 15776 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-18 15:48:46.172096 | 15776 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-18 15:48:46.332781 | 15776 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-19 00:48:45.992494 | 24930 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-19 00:48:46.064604 | 24930 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-21 00:48:47.708846 | 14091 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-21 00:48:47.812423 | 14091 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-22 00:48:46.552925 |  8539 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-22 00:48:53.25854  |  8539 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-22 00:48:53.261857 |  8539 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-22 00:48:55.625125 |  8539 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
(12 rows)