Skip to content

Commit 9b1d8e6

Browse files
Add max_batches to compact_chunk
Adds a max_batches parameter to the compaction policy and compact_chunk function. When set, compaction stops between merge groups after decompressing that many batches, bounding work per chunk. Zero means unlimited.
1 parent bf09c2c commit 9b1d8e6

14 files changed

Lines changed: 428 additions & 20 deletions

File tree

.unreleased/pr_10266

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implements: #10266 Add max_batches to compact_chunk

sql/maintenance_utils.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ CREATE OR REPLACE FUNCTION _timescaledb_functions.recompress_chunk_segmentwise(
9090
) RETURNS REGCLASS AS '@MODULE_PATHNAME@', 'ts_recompress_chunk_segmentwise' LANGUAGE C STRICT VOLATILE;
9191

9292
CREATE OR REPLACE FUNCTION _timescaledb_functions.compact_chunk(
93-
uncompressed_chunk REGCLASS
93+
uncompressed_chunk REGCLASS,
94+
max_batches INTEGER DEFAULT 0
9495
) RETURNS REGCLASS AS '@MODULE_PATHNAME@', 'ts_compact_chunk' LANGUAGE C STRICT VOLATILE;
9596

9697
-- find the index on the compressed chunk that can be used to recompress efficiently

sql/policy_api.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ CREATE OR REPLACE FUNCTION @extschema@.add_compaction_policy(
5252
initial_start TIMESTAMPTZ = NULL,
5353
timezone TEXT = NULL,
5454
max_chunks INTEGER = NULL,
55+
max_batches INTEGER = NULL,
5556
inactive_for INTERVAL = NULL
5657
) RETURNS INTEGER
5758
AS '@MODULE_PATHNAME@', 'ts_policy_compaction_add'

sql/policy_internal.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ AS $$
2727
DECLARE
2828
htid INTEGER;
2929
max_chunks INTEGER := 0;
30+
max_batches INTEGER := 0;
3031
numchunks INTEGER := 0;
3132
processed INTEGER := 0;
3233
chunk_rec RECORD;
@@ -53,6 +54,7 @@ BEGIN
5354

5455
verbose_log := COALESCE(jsonb_object_field_text(config, 'verbose_log')::BOOLEAN, FALSE);
5556
max_chunks := COALESCE(jsonb_object_field_text(config, 'max_chunks')::INTEGER, 0);
57+
max_batches := COALESCE(jsonb_object_field_text(config, 'max_batches')::INTEGER, 0);
5658
-- when set, skip chunks written within this window; NULL disables the gate
5759
inactive_for := jsonb_object_field_text(config, 'inactive_for')::INTERVAL;
5860

@@ -74,7 +76,7 @@ BEGIN
7476
WHERE s.last_update >= now() - inactive_for))
7577
LOOP
7678
BEGIN
77-
PERFORM _timescaledb_functions.compact_chunk(chunk_rec.relid);
79+
PERFORM _timescaledb_functions.compact_chunk(chunk_rec.relid, max_batches);
7880
numchunks := numchunks + 1;
7981
EXCEPTION WHEN OTHERS THEN
8082
GET STACKED DIAGNOSTICS

sql/updates/reverse-dev.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ DROP FUNCTION IF EXISTS _timescaledb_functions.hypertable_status_text(int);
33
DROP FUNCTION IF EXISTS _timescaledb_functions.hypertable_status_text(regclass);
44
DROP FUNCTION IF EXISTS _timescaledb_functions.decompress_batch(record);
55
DROP FUNCTION IF EXISTS _timescaledb_functions.estimate_uncompressed_size(regclass, double precision);
6-
DROP FUNCTION IF EXISTS _timescaledb_functions.compact_chunk(REGCLASS);
6+
DROP FUNCTION IF EXISTS _timescaledb_functions.compact_chunk(REGCLASS, INTEGER);
77
DROP PROCEDURE IF EXISTS _timescaledb_functions.policy_compression_execute(INTEGER, INTEGER, ANYELEMENT, INTEGER, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN);
88

99
--
@@ -243,7 +243,7 @@ ALTER TABLE _timescaledb_catalog.compression_chunk_size
243243

244244
-- Remove compaction policy jobs since the policy does not exist in the older version.
245245
DELETE FROM _timescaledb_config.bgw_job WHERE proc_schema = '_timescaledb_functions' AND proc_name = 'policy_compaction';
246-
DROP FUNCTION IF EXISTS @extschema@.add_compaction_policy(REGCLASS, BOOL, INTERVAL, TIMESTAMPTZ, TEXT, INTEGER, INTERVAL);
246+
DROP FUNCTION IF EXISTS @extschema@.add_compaction_policy(REGCLASS, BOOL, INTERVAL, TIMESTAMPTZ, TEXT, INTEGER, INTEGER, INTERVAL);
247247
DROP FUNCTION IF EXISTS @extschema@.remove_compaction_policy(REGCLASS, BOOL);
248248
DROP PROCEDURE IF EXISTS _timescaledb_functions.policy_compaction(INTEGER, JSONB);
249249
DROP FUNCTION IF EXISTS _timescaledb_functions.policy_compaction_check(JSONB);

tsl/src/bgw_policy/compaction_api.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ policy_compaction_check(PG_FUNCTION_ARGS)
7070
POL_COMPACTION_CONF_KEY_MAX_CHUNKS)));
7171
}
7272

73+
int32 max_batches =
74+
ts_jsonb_get_int32_field(config, POL_COMPACTION_CONF_KEY_MAX_BATCHES, &found);
75+
if (found && max_batches < 0)
76+
{
77+
ereport(ERROR,
78+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79+
errmsg("%s must be greater than or equal to 0",
80+
POL_COMPACTION_CONF_KEY_MAX_BATCHES)));
81+
}
82+
7383
/* Reading the field parses it, raising on a malformed interval */
7484
Interval *inactive_for =
7585
ts_jsonb_get_interval_field(config, POL_COMPACTION_CONF_KEY_INACTIVE_FOR);
@@ -106,7 +116,8 @@ policy_compaction_add(PG_FUNCTION_ARGS)
106116
bool if_not_exists = PG_GETARG_BOOL(1);
107117
bool user_defined_schedule_interval = !PG_ARGISNULL(2);
108118
int32 max_chunks = PG_ARGISNULL(5) ? 0 : PG_GETARG_INT32(5);
109-
Interval *inactive_for = PG_ARGISNULL(6) ? NULL : PG_GETARG_INTERVAL_P(6);
119+
int32 max_batches = PG_ARGISNULL(6) ? 0 : PG_GETARG_INT32(6);
120+
Interval *inactive_for = PG_ARGISNULL(7) ? NULL : PG_GETARG_INTERVAL_P(7);
110121
Cache *hcache;
111122
Hypertable *ht;
112123
int32 hypertable_id;
@@ -137,6 +148,13 @@ policy_compaction_add(PG_FUNCTION_ARGS)
137148
errmsg("max_chunks must be greater than or equal to 0")));
138149
}
139150

151+
if (max_batches < 0)
152+
{
153+
ereport(ERROR,
154+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
155+
errmsg("max_batches must be greater than or equal to 0")));
156+
}
157+
140158
ht = ts_hypertable_cache_get_cache_and_entry(ht_oid, CACHE_FLAG_NONE, &hcache);
141159
Assert(ht != NULL);
142160
hypertable_id = ht->fd.id;
@@ -205,6 +223,10 @@ policy_compaction_add(PG_FUNCTION_ARGS)
205223
{
206224
ts_jsonb_add_int32(&parse_state, POL_COMPACTION_CONF_KEY_MAX_CHUNKS, max_chunks);
207225
}
226+
if (max_batches > 0)
227+
{
228+
ts_jsonb_add_int32(&parse_state, POL_COMPACTION_CONF_KEY_MAX_BATCHES, max_batches);
229+
}
208230
if (inactive_for != NULL)
209231
{
210232
ts_jsonb_add_interval(&parse_state, POL_COMPACTION_CONF_KEY_INACTIVE_FOR, inactive_for);

tsl/src/bgw_policy/policies_v2.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#define POLICY_COMPACTION_PROC_NAME "policy_compaction"
4343
#define POLICY_COMPACTION_CHECK_NAME "policy_compaction_check"
4444
#define POL_COMPACTION_CONF_KEY_MAX_CHUNKS "max_chunks"
45+
#define POL_COMPACTION_CONF_KEY_MAX_BATCHES "max_batches"
4546
#define POL_COMPACTION_CONF_KEY_INACTIVE_FOR "inactive_for"
4647

4748
#define SHOW_POLICY_KEY_POLICY_NAME "policy_name"

tsl/src/compression/recompress.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,17 @@ static bool batches_overlap_firstlast(RecompressContext *recompress_ctx, Datum *
155155
static void decompress_batch_to_tuplesort(TupleTableSlot *slot, TupleDesc tupdesc,
156156
RowDecompressor *decompressor,
157157
Tuplesortstate *recompress_tuplesortstate,
158-
Relation compressed_chunk_rel, Snapshot snapshot);
158+
Relation compressed_chunk_rel, Snapshot snapshot,
159+
int *processed_batches);
159160
static bool compact_chunk_find_overlapping_batches(Relation compressed_chunk_rel,
160161
IndexScanDesc index_scan,
161162
RecompressContext *recompress_ctx,
162163
CompactChunkScanState *state);
163164
static bool compact_chunk_recompress_overlapping_batches(
164165
Relation compressed_chunk_rel, IndexScanDesc index_scan, Snapshot snapshot,
165166
RecompressContext *recompress_ctx, CompactChunkScanState *state, RowCompressor *compressor,
166-
RowDecompressor *decompressor, Tuplesortstate *recompress_tuplesortstate, BulkWriter *writer);
167+
RowDecompressor *decompressor, Tuplesortstate *recompress_tuplesortstate, BulkWriter *writer,
168+
int max_batches);
167169
static void try_updating_chunk_status(Chunk *uncompressed_chunk, Relation uncompressed_chunk_rel);
168170

169171
/*
@@ -272,7 +274,10 @@ tsl_compact_chunk(PG_FUNCTION_ARGS)
272274
ts_chunk_get_table_name(chunk))));
273275
}
274276

275-
uncompressed_relid = compact_chunk_impl(chunk);
277+
int max_batches = PG_GETARG_INT32(1);
278+
Assert(max_batches >= 0);
279+
280+
uncompressed_relid = compact_chunk_impl(chunk, max_batches);
276281

277282
PG_RETURN_OID(uncompressed_relid);
278283
}
@@ -1061,7 +1066,8 @@ static void
10611066
decompress_batch_to_tuplesort(TupleTableSlot *slot, TupleDesc tupdesc,
10621067
RowDecompressor *decompressor,
10631068
Tuplesortstate *recompress_tuplesortstate,
1064-
Relation compressed_chunk_rel, Snapshot snapshot)
1069+
Relation compressed_chunk_rel, Snapshot snapshot,
1070+
int *processed_batches)
10651071
{
10661072
bool should_free;
10671073
HeapTuple compressed_tuple = ExecFetchSlotHeapTuple(slot, false, &should_free);
@@ -1092,6 +1098,11 @@ decompress_batch_to_tuplesort(TupleTableSlot *slot, TupleDesc tupdesc,
10921098
{
10931099
heap_freetuple(compressed_tuple);
10941100
}
1101+
1102+
if (processed_batches)
1103+
{
1104+
(*processed_batches)++;
1105+
}
10951106
}
10961107

10971108
/*
@@ -1169,14 +1180,17 @@ static bool
11691180
compact_chunk_recompress_overlapping_batches(
11701181
Relation compressed_chunk_rel, IndexScanDesc index_scan, Snapshot snapshot,
11711182
RecompressContext *recompress_ctx, CompactChunkScanState *state, RowCompressor *compressor,
1172-
RowDecompressor *decompressor, Tuplesortstate *recompress_tuplesortstate, BulkWriter *writer)
1183+
RowDecompressor *decompressor, Tuplesortstate *recompress_tuplesortstate, BulkWriter *writer,
1184+
int max_batches)
11731185
{
11741186
TupleTableSlot *previous_compressed_slot = table_slot_create(compressed_chunk_rel, NULL);
11751187
TupleTableSlot *compressed_slot = table_slot_create(compressed_chunk_rel, NULL);
11761188

11771189
TupleDesc compressed_rel_tupdesc = RelationGetDescr(compressed_chunk_rel);
11781190
bool overlapping = false;
11791191
bool found_overlaps = false;
1192+
/* Counts decompressed batches */
1193+
int processed_batches = 0;
11801194

11811195
/*
11821196
* The find pass identified the first overlapping pair. Fetch both batches by
@@ -1200,7 +1214,8 @@ compact_chunk_recompress_overlapping_batches(
12001214
decompressor,
12011215
recompress_tuplesortstate,
12021216
compressed_chunk_rel,
1203-
snapshot);
1217+
snapshot,
1218+
&processed_batches);
12041219

12051220
found = table_index_fetch_tuple(index_scan->xs_heapfetch,
12061221
&state->previous_tid,
@@ -1214,7 +1229,8 @@ compact_chunk_recompress_overlapping_batches(
12141229
decompressor,
12151230
recompress_tuplesortstate,
12161231
compressed_chunk_rel,
1217-
snapshot);
1232+
snapshot,
1233+
&processed_batches);
12181234

12191235
overlapping = true;
12201236
found_overlaps = true;
@@ -1248,6 +1264,12 @@ compact_chunk_recompress_overlapping_batches(
12481264
compressor,
12491265
writer);
12501266
overlapping = false;
1267+
1268+
/* Check only after a full flush so we never leave partial work */
1269+
if (max_batches > 0 && processed_batches >= max_batches)
1270+
{
1271+
break;
1272+
}
12511273
}
12521274

12531275
ItemPointerCopy(&index_scan->xs_heaptid, &state->previous_tid);
@@ -1285,7 +1307,8 @@ compact_chunk_recompress_overlapping_batches(
12851307
decompressor,
12861308
recompress_tuplesortstate,
12871309
compressed_chunk_rel,
1288-
snapshot);
1310+
snapshot,
1311+
&processed_batches);
12891312

12901313
overlapping = true;
12911314
found_overlaps = true;
@@ -1296,7 +1319,8 @@ compact_chunk_recompress_overlapping_batches(
12961319
decompressor,
12971320
recompress_tuplesortstate,
12981321
compressed_chunk_rel,
1299-
snapshot);
1322+
snapshot,
1323+
&processed_batches);
13001324

13011325
CommandCounterIncrement();
13021326
}
@@ -1307,6 +1331,12 @@ compact_chunk_recompress_overlapping_batches(
13071331
recompress_segment(recompress_tuplesortstate, compressed_chunk_rel, compressor, writer);
13081332
overlapping = false;
13091333
CommandCounterIncrement();
1334+
1335+
/* Check only after a full flush so we never leave partial work */
1336+
if (max_batches > 0 && processed_batches >= max_batches)
1337+
{
1338+
break;
1339+
}
13101340
}
13111341

13121342
ItemPointerCopy(&index_scan->xs_heaptid, &state->previous_tid);
@@ -1325,7 +1355,7 @@ compact_chunk_recompress_overlapping_batches(
13251355
}
13261356

13271357
Oid
1328-
compact_chunk_impl(Chunk *uncompressed_chunk)
1358+
compact_chunk_impl(Chunk *uncompressed_chunk, int max_batches)
13291359
{
13301360
Oid uncompressed_chunk_id = uncompressed_chunk->fd.relid;
13311361

@@ -1496,7 +1526,8 @@ compact_chunk_impl(Chunk *uncompressed_chunk)
14961526
&compressor,
14971527
&decompressor,
14981528
recompress_tuplesortstate,
1499-
&writer);
1529+
&writer,
1530+
max_batches);
15001531
row_compressor_close(&compressor);
15011532
row_decompressor_close(&decompressor);
15021533
tuplesort_end(recompress_tuplesortstate);

tsl/src/compression/recompress.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ extern Datum tsl_recompress_chunk_segmentwise(PG_FUNCTION_ARGS);
4343
extern Datum tsl_compact_chunk(PG_FUNCTION_ARGS);
4444

4545
void recompress_chunk_segmentwise_impl(Chunk *chunk, bool fullrecompress);
46-
Oid compact_chunk_impl(Chunk *chunk);
46+
Oid compact_chunk_impl(Chunk *chunk, int max_batches);
4747
bool recompress_chunk_in_memory_impl(Chunk *uncompressed_chunk);
4848
void rebuild_sparse_index_impl(Chunk *uncompressed_chunk, bool force);
4949

0 commit comments

Comments
 (0)