Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5e3277f
Refactoring of fault_injector to support former debug_dtm_action points
vbwagner Aug 14, 2025
021fccd
Replace debug_dtm_action fault injection points with faultinjector calls
vbwagner Aug 14, 2025
7e69c02
Remove debug_dtm_action* GUC variables
vbwagner Aug 14, 2025
eb7eb5d
Refactor tests which use debug_dtm_action
vbwagner Aug 14, 2025
5102b1e
Expected results for test wihich use debug_dtm_actions
vbwagner Aug 14, 2025
2c742a1
Improved README for gp_inject_fault extension
Aug 22, 2025
e6e1818
Add symbols, changed by the patch to .abi-check/ignore
Aug 22, 2025
922ea74
Fix list of changed symbols for abi-checker
Aug 22, 2025
ce230ac
Remove mention of debug_dtm_action vars
Aug 25, 2025
15e8512
Fix formatting problems found during review
Aug 25, 2025
be10025
Reset fault inserted by twophase_pqexecparams
Aug 25, 2025
d259baf
Fix typos in README file and test comments
Aug 25, 2025
fe9a6c2
pgindent additions to faultinjector.c
Aug 27, 2025
3c9a9b5
Remove declarations of Debug_dtm_action vars from guc.h
Aug 27, 2025
070be33
Fix typo in the comments in gp_inject_fault--1.0.sql
Aug 27, 2025
8171926
Add empty lines to make code more readable
Aug 27, 2025
00535a0
Fix indentation and typos in the faultinjector.h
Aug 27, 2025
f663762
Remove some nonsignificant changes from gp_inject_fault README
Aug 27, 2025
084e58e
Improve tests and diffs readability
Aug 27, 2025
e8017be
Remove debug_dtm_action-related types
Aug 27, 2025
8a34f88
Improve formattig of sql code of isolation2 test
Aug 27, 2025
e6d6b79
Undo removal of end-of-line whitespace to reduce diff size
Aug 27, 2025
7679d37
Undo removal whitespace from end-of-line in postgres.c
Aug 27, 2025
15abc06
Fix formatting of FI_DDL_STATEMENT macros
Aug 27, 2025
2f29b59
Fix minor formatting issues.
Sep 1, 2025
166095e
Minimize patch size by removing differences in out and sql
Sep 2, 2025
bef70ec
Restore end-of-line space in expected test result
Sep 4, 2025
471eb63
Move function description comments to c-file
Sep 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .abi-check/7.2.0_arenadata7/postgres.symbols.ignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
ConfigureNamesInt_gp
ConfigureNamesEnum_gp
ConfigureNamesString_gp
ConfigureNamesBool_gp
transformCreateSchemaStmt
FaultInjector_InjectFaultIfSet_out_of_line
FaultInjector_InjectFaultIfSet_out_of_line_DTX
FaultInjector_InjectFaultIfSet_out_of_line_SQL
InjectFault
Debug_dtm_action
Debug_dtm_action_nestinglevel
Debug_dtm_action_primary
Debug_dtm_action_protocol
Debug_dtm_action_segment
Debug_dtm_action_sql_command_tag
Debug_dtm_action_target
2 changes: 2 additions & 0 deletions .abi-check/7.2.0_arenadata7/postgres.types.ignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
struct RunningTransactionsData
struct FaultInjectorEntry_s
DDLStatement_e
111 changes: 105 additions & 6 deletions gpcontrib/gp_inject_fault/README
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ Fault Injection Framework

Fault is defined as a point of interest in the source code with an
associated action to be taken when that point is hit during execution.
Fault points are defined in the instrumented code using
SIMPLE_FAULT_INJECTOR() macro. A fault point is identifed by a name.
Fault points are defined in the instrumented code. Simplest way is to use
SIMPLE_FAULT_INJECTOR() macro. There are more complex ways, which allows
to pass some state variables to fault injection point, and inject fault
only if certain conditions are met. See section **Fault Injection Macros** below.

A fault point is identified by a name.
This module provides an interface to inject a predefined fault point
into a running Greenplum cluster by associating an action with the
fault point. Action can be error, panic, sleep, skip, infinite_loop,
Expand Down Expand Up @@ -60,7 +64,8 @@ several more parameters. For example:
'' /* DDL */, '' /* database name */,
'my_table' /* table name */,
1 /* start occurrence */, 10 /* end occurrence */,
0 /* */, dbid) from
0 /* extra arg */, dbid,
-1 /*gp session id*/, 0 /*nesting level*/) from
gp_segment_configuration where content=1 and role='p';

The above command sets heap_insert fault such that the inserting
Expand All @@ -70,6 +75,11 @@ fault point, only if the relation being inserted to has the name
stop triggering. The 11th transaction to insert into my_table will
continue the insert as if no fault was injected.

Interpretation of the extraArg argument depends on fault type.

There are few versions of this function with lesser number of
parameters. See gp_inject_fault--1.0.sql.

Fault actions
-------------

Expand All @@ -86,7 +96,8 @@ panic
elog(PANIC)

sleep
sleep for specified amount of time
sleep for amount of time, specified as extraArg parameter, in
seconds.

infinite_loop
loop until query cancel or terminate signal is received
Expand All @@ -107,11 +118,17 @@ skip
}

reset
remove a previously injected fault
remove a previously injected fault. It is neccessary to reset fault
before injection of other fault with same name and different
parameters.

segv
crash the backend process due to SIGSEGV

exit_no_callbacks
terminate backend using C _exit() function with return code from
extraArg argument.

interrupt
simulate cancel interrupt arrival, such that the next
interrupt processing cycle will cancel the query
Expand All @@ -133,6 +150,88 @@ status
longer be taken because the fault point has been reached maximum
number of times during execution.

Fault injection parameters
--------------------------

Following parameters can be used for fault injection function:

* faultname text - mandatory. Name of fault injection point as defined
in the code.
* type text - mandatory. See previous section
* ddl text - SQL operator name or DTX protocol command which should be
processed otherwise fault wouldn't be injected. Pass empty string, if
you don't want limit injection by particular operator. See
`faultinjector_lists.h` for list of allowed operators. This
parameter makes sense only if injection point in the code was defined
by macro which accepts `ddlStatement` parameter and some meaningful
value is passed to it.
* database text - database name which should be processed. Makes sense
only if you know that database name was passed to injection point in
the code
* tablename text - table name which should be processed. Same
conditions as in previous case stands.
* start_occurrence int4 - number of times injection point is hit when
first fault is injected.
* end_occurrence int4 - number of times injection point is hit when
last fault is injected. Use -1 if you don't want to limit number of
hits.
* extra_arg int4 - use of this argument depends on fault type. For type
'sleep' it is number of seconds to sleep,
* db_id int4 - db id (from dbid column of gp_segment_configuration table)
mandatory. See above for examples of SQL queries.
* gp_session_id int4 - value of global variable gp_session_id (see
src/include/cdb/cdbvars.h) If you don't want to limit faults
to particular session, use -1 (value of C constant InvalidGpSessionId).
* nestinglevel int4 level of transaction nesting. Makes sense only if
its value is passed to the injection point in the code. Use 0 if you
don't want limit level, or just use SQL function without this
parameter.

Fault Injection Macros
----------------------

In the C code fault injection point is marked by adding one of macros,
defined in the faultinjector.h. All macros end up with call of single
function FaultInjector_InjectFaultIfSet_out_of_line, but this function
is always wrapped with some macro to avoid function call overhead if no
fault was injected into particular segment.

```
FaultInjector_InjectFaultIfSet(const char *faultName,
DDLStatement_e ddlstatement,
const char* databaseName,
const char* tableName)
```

Allows to pass database name and tablename. ddlStatement should be
passed as element of enumeration DDLStatement_e

```
FaultInjector_InjectFaultIfSet_SQL(
const char faultName,
const char SQLStatement,
int nestingLevel)
```

Allows to specify SQL statement as commandTag string which can be
derived from parsetree node via CreateCommandTag() function (see
src/include/tcop/utility.h). For MPPEXEC statements command tags are
created by explicit code at the beginning of exec_mpp_query() function.

```
FaultInjector_InjectFaultIfSet_DTX(
const char * faultName,
DtxProtocolCommand dtxProtocol,
int nestingLevel)
```
Allows to pass DTX protocol command as value of enumeration
DtxProtocolCommand and subtransaction nesting level.

```
SIMPLE_FAULT_INJECTOR(const char *faultName)
```
Already discussed in the introduction section.

NOTES
-----

Expand All @@ -144,5 +243,5 @@ many backends, such as in parallel regress tests.
associates actions with them.

* We let some background process ignore all but a few faults. If one wants
to test fault injection in background processese, add the exception in
to test fault injection in background processes, add the exception in
checkBgProcessSkipFault().
29 changes: 23 additions & 6 deletions gpcontrib/gp_inject_fault/gp_inject_fault--1.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,28 @@ CREATE FUNCTION @extschema@.gp_inject_fault(
end_occurrence int4,
extra_arg int4,
db_id int4,
gp_session_id int4)
gp_session_id int4,
nestinglevel int4)
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C VOLATILE STRICT NO SQL;

-- simpler version without nesting level

CREATE FUNCTION @extschema@.gp_inject_fault(
faultname text,
type text,
ddl text,
database text,
tablename text,
start_occurrence int4,
end_occurrence int4,
extra_arg int4,
db_id int4,
gp_session_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 0) $$
LANGUAGE SQL;
-- Simpler version, without specific session id.
CREATE FUNCTION @extschema@.gp_inject_fault(
faultname text,
Expand All @@ -31,7 +48,7 @@ CREATE FUNCTION @extschema@.gp_inject_fault(
extra_arg int4,
db_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, $2, $3, $4, $5, $6, $7, $8, $9, -1) $$
AS $$ select @extschema@.gp_inject_fault($1, $2, $3, $4, $5, $6, $7, $8, $9, -1, 0) $$
LANGUAGE SQL;

-- Simpler version, trigger only one time, occurrence start at 1 and
Expand All @@ -41,7 +58,7 @@ CREATE FUNCTION @extschema@.gp_inject_fault(
type text,
db_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, 1, 0, $3, -1) $$
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, 1, 0, $3, -1, 0) $$
LANGUAGE SQL;

-- Simpler version, trigger only one time, occurrence start at 1 and
Expand All @@ -52,7 +69,7 @@ CREATE FUNCTION @extschema@.gp_inject_fault(
db_id int4,
gp_session_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, 1, 0, $3, $4) $$
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, 1, 0, $3, $4, 0) $$
LANGUAGE SQL;

-- Simpler version, always trigger until fault is reset.
Expand All @@ -61,7 +78,7 @@ CREATE FUNCTION @extschema@.gp_inject_fault_infinite(
type text,
db_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, -1, 0, $3, -1) $$
AS $$ select @extschema@.gp_inject_fault($1, $2, '', '', '', 1, -1, 0, $3, -1, 0) $$
LANGUAGE SQL;

-- Simpler version to avoid confusion for wait_until_triggered fault.
Expand All @@ -72,7 +89,7 @@ CREATE FUNCTION @extschema@.gp_wait_until_triggered_fault(
numtimestriggered int4,
db_id int4)
RETURNS text
AS $$ select @extschema@.gp_inject_fault($1, 'wait_until_triggered', '', '', '', 1, 1, $2, $3, -1) $$
AS $$ select @extschema@.gp_inject_fault($1, 'wait_until_triggered', '', '', '', 1, 1, $2, $3, -1, 0) $$
LANGUAGE SQL;

CREATE OR REPLACE FUNCTION @extschema@.insert_noop_xlog_record()
Expand Down
13 changes: 8 additions & 5 deletions gpcontrib/gp_inject_fault/gp_inject_fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ gp_inject_fault(PG_FUNCTION_ARGS)
* others mean the fault could only be triggered by the specific session.
*/
int gpSessionid = PG_GETARG_INT32(9);
int nestingLevel = PG_GETARG_INT32(10);
char *hostname;
int port;
char *response;
Expand All @@ -110,8 +111,9 @@ gp_inject_fault(PG_FUNCTION_ARGS)
if (GpIdentity.dbid == dbid)
{
response = InjectFault(
faultName, type, ddlStatement, databaseName,
tableName, startOccurrence, endOccurrence, extraArg, gpSessionid);
faultName, type, ddlStatement, databaseName, tableName,
startOccurrence, endOccurrence, extraArg, gpSessionid,
nestingLevel);
if (!response)
elog(ERROR, "failed to inject fault locally (dbid %d)", dbid);
if (strncmp(response, "Success:", strlen("Success:")) != 0)
Expand Down Expand Up @@ -142,16 +144,17 @@ gp_inject_fault(PG_FUNCTION_ARGS)
databaseName = "#";
if (!tableName || tableName[0] == '\0')
tableName = "#";
snprintf(msg, 1024, "faultname=%s type=%s ddl=%s db=%s table=%s "
"start=%d end=%d extra=%d sid=%d ",
snprintf(msg, 1024, "faultname=%s type=%s ddl='%s' db=%s table=%s "
Comment thread
RekGRpth marked this conversation as resolved.
"start=%d end=%d extra=%d sid=%d nestinglevel=%d",
faultName, type,
ddlStatement,
databaseName,
tableName,
startOccurrence,
endOccurrence,
extraArg,
gpSessionid);
gpSessionid,
nestingLevel);
res = PQexec(conn, msg);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
elog(ERROR, "failed to inject fault: %s", PQerrorMessage(conn));
Expand Down
Loading