提交 6c16abca 编写于 作者: A Ashwin Agrawal

Provide workaround to reclaim space on truncate cmd in sub-transaction

As discussed in gpdb-users thread [1], currently no mechanism exist to
reclaim disk space for a table created and truncated or dropped
iteratively in a plpython function. PostgreSQL 11 provides
`plpy.commit()` using which this can be achieved. But in absence of
same need to provide some mechanism as MADlib has requirement for this
functionality for GPDB6 and forward.

Hence, considering the requirement as interim work-around adding guc
to be able to perform unsafe truncate instead of safe truncate from
plpython execute function. Setting the GUC can force unsafe truncation
only if

- inside sub-transaction and not in top transaction
- table was created somewhere within this transactions scope and not
  outside of it

The GUC will be set in the plpython udf and if any `plpy.execute()`
errors out the top transaction will also rollback. The GUC can't be
set in postgresql.conf file. Also, added description to warn the guc
is not for general purpose use and is developer only guc.

Added test showcases in simple form the scenario.

[1] https://groups.google.com/a/greenplum.org/d/msg/gpdb-users/YCtI4oUA3r0/t0CzhtL6AQAJReviewed-by: NSoumyadeep Chakraborty <sochakraborty@pivotal.io>
上级 4ff5b9f1
......@@ -1678,6 +1678,8 @@ ExecuteTruncate(TruncateStmt *stmt)
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);
bool inSubTransaction = mySubid != TopSubTransactionId;
bool createdInThisTransactionScope = rel->rd_createSubid != InvalidSubTransactionId;
Assert(CheckExclusiveAccess(rel));
......@@ -1687,9 +1689,20 @@ ExecuteTruncate(TruncateStmt *stmt)
* a new relfilenode in the current (sub)transaction, then we can just
* truncate it in-place, because a rollback would cause the whole
* table or the current physical file to be thrown away anyway.
*
* GPDB_11_MERGE_FIXME: Remove this guc and related code once we get
* plpy.commit().
*
* GPDB: Using GUC dev_opt_unsafe_truncate_in_subtransaction can force
* unsafe truncation only if
* - inside sub-transaction and not in top transaction
* - table was created somewhere within this transaction scope
*/
if (rel->rd_createSubid == mySubid ||
rel->rd_newRelfilenodeSubid == mySubid)
rel->rd_newRelfilenodeSubid == mySubid ||
(dev_opt_unsafe_truncate_in_subtransaction &&
inSubTransaction && createdInThisTransactionScope))
{
/* Immediate, non-rollbackable truncation is OK */
heap_truncate_one_rel(rel);
......
......@@ -108,6 +108,7 @@ bool gp_guc_need_restore = false;
char *Debug_dtm_action_sql_command_tag;
bool dev_opt_unsafe_truncate_in_subtransaction = false;
bool Debug_print_full_dtm = false;
bool Debug_print_snapshot_dtm = false;
bool Debug_disable_distributed_snapshot = false;
......@@ -1098,6 +1099,21 @@ struct config_bool ConfigureNamesBool_gp[] =
NULL, NULL, NULL
},
{
{"dev_opt_unsafe_truncate_in_subtransaction", PGC_USERSET, DEVELOPER_OPTIONS,
gettext_noop("Pick unsafe truncate instead of safe truncate inside sub-transaction."),
gettext_noop("Usage of this GUC is strongly discouraged and only "
"should be used after understanding the impact of using "
"the same. Setting the GUC comes with cost of losing "
"table data on truncate command despite sub-transaction "
"rollback for table created within transaction."),
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_DISALLOW_IN_AUTO_FILE
},
&dev_opt_unsafe_truncate_in_subtransaction,
false,
NULL, NULL, NULL
},
{
{"debug_print_full_dtm", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Prints full DTM information to server log."),
......
......@@ -267,6 +267,7 @@ extern bool Debug_print_parse;
extern bool Debug_print_rewritten;
extern bool Debug_pretty_print;
extern bool dev_opt_unsafe_truncate_in_subtransaction;
extern bool Debug_print_full_dtm;
extern bool Debug_print_snapshot_dtm;
extern bool Debug_disable_distributed_snapshot;
......
......@@ -4,6 +4,7 @@
"coredump_on_memerror",
"DateStyle",
"default_tablespace",
"dev_opt_unsafe_truncate_in_subtransaction",
"dml_ignore_target_partition_check",
"execute_pruned_plan",
"explain_memory_verbosity",
......
......@@ -299,3 +299,40 @@ NOTICE: command without clusterwide effect
HINT: Concider alternatives as DEALLOCATE ALL, or DISCARD TEMP if a clusterwide effect is desired.
CREATE TEMP TABLE reset_test ( data text ) ON COMMIT PRESERVE ROWS;
ERROR: relation "reset_test" already exists (seg0 127.0.1.1:7002 pid=26153)
-- test for guc dev_opt_unsafe_truncate_in_subtransaction
-- start_ignore
CREATE LANGUAGE plpythonu;
-- end_ignore
CREATE OR REPLACE FUNCTION run_all_in_one() RETURNS VOID AS
$$
plpy.execute('CREATE TABLE unsafe_truncate(a int, b int) DISTRIBUTED BY (a)')
plpy.execute('INSERT INTO unsafe_truncate SELECT * FROM generate_series(1, 10)')
for i in range(1,4):
plpy.execute('UPDATE unsafe_truncate SET b = b + 1')
plpy.execute('CREATE TABLE foobar AS SELECT * FROM unsafe_truncate DISTRIBUTED BY (a)')
before_truncate = plpy.execute('SELECT relfilenode FROM gp_dist_random(\'pg_class\') WHERE relname=\'unsafe_truncate\' ORDER BY gp_segment_id')
plpy.execute('truncate unsafe_truncate')
after_truncate = plpy.execute('SELECT relfilenode FROM gp_dist_random(\'pg_class\') WHERE relname=\'unsafe_truncate\' ORDER BY gp_segment_id')
plpy.execute('DROP TABLE unsafe_truncate')
plpy.execute('ALTER TABLE foobar RENAME TO unsafe_truncate')
if before_truncate[0]['relfilenode'] == after_truncate[0]['relfilenode']:
plpy.info('iteration:%d unsafe truncate performed' % (i))
else:
plpy.info('iteration:%d safe truncate performed' % (i))
plpy.execute('SET dev_opt_unsafe_truncate_in_subtransaction TO ON')
plpy.execute('DROP TABLE unsafe_truncate')
plpy.execute('RESET dev_opt_unsafe_truncate_in_subtransaction')
$$ language plpythonu;
select run_all_in_one();
INFO: iteration:1 safe truncate performed
INFO: iteration:2 unsafe truncate performed
INFO: iteration:3 unsafe truncate performed
run_all_in_one
----------------
(1 row)
......@@ -188,3 +188,34 @@ SELECT * FROM reset_test;
-- table will not be dropped in the segments.
DISCARD ALL;
CREATE TEMP TABLE reset_test ( data text ) ON COMMIT PRESERVE ROWS;
-- test for guc dev_opt_unsafe_truncate_in_subtransaction
-- start_ignore
CREATE LANGUAGE plpythonu;
-- end_ignore
CREATE OR REPLACE FUNCTION run_all_in_one() RETURNS VOID AS
$$
plpy.execute('CREATE TABLE unsafe_truncate(a int, b int) DISTRIBUTED BY (a)')
plpy.execute('INSERT INTO unsafe_truncate SELECT * FROM generate_series(1, 10)')
for i in range(1,4):
plpy.execute('UPDATE unsafe_truncate SET b = b + 1')
plpy.execute('CREATE TABLE foobar AS SELECT * FROM unsafe_truncate DISTRIBUTED BY (a)')
before_truncate = plpy.execute('SELECT relfilenode FROM gp_dist_random(\'pg_class\') WHERE relname=\'unsafe_truncate\' ORDER BY gp_segment_id')
plpy.execute('truncate unsafe_truncate')
after_truncate = plpy.execute('SELECT relfilenode FROM gp_dist_random(\'pg_class\') WHERE relname=\'unsafe_truncate\' ORDER BY gp_segment_id')
plpy.execute('DROP TABLE unsafe_truncate')
plpy.execute('ALTER TABLE foobar RENAME TO unsafe_truncate')
if before_truncate[0]['relfilenode'] == after_truncate[0]['relfilenode']:
plpy.info('iteration:%d unsafe truncate performed' % (i))
else:
plpy.info('iteration:%d safe truncate performed' % (i))
plpy.execute('SET dev_opt_unsafe_truncate_in_subtransaction TO ON')
plpy.execute('DROP TABLE unsafe_truncate')
plpy.execute('RESET dev_opt_unsafe_truncate_in_subtransaction')
$$ language plpythonu;
select run_all_in_one();
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册