diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 46c3af066c9..7111aeb0ed6 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -103,6 +103,10 @@ int client_connection_check_interval = 0; /* flags for non-system relation kinds to restrict use */ int restrict_nonsystem_relation_kind; +#ifdef USE_INJECTION_POINTS +/* Counter which can show us if we are in SMGR */ +int inside_smgr_api = 0; +#endif /* ---------------- * private typedefs etc diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 33a9e4dc486..727f48ac77c 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -28,6 +28,10 @@ #include "datatype/timestamp.h" /* for TimestampTz */ #include "pgtime.h" /* for pg_time_t */ +#ifdef USE_INJECTION_POINTS +#include "utils/injection_point.h" +#endif + #define InvalidPid (-1) @@ -123,18 +127,30 @@ extern process_interrupts_callback_t ProcessInterruptsCallback; unlikely(InterruptPending)) #endif +/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */ +#define INTERRUPTS_CAN_BE_PROCESSED() \ + (InterruptHoldoffCount == 0 && CritSectionCount == 0 && \ + QueryCancelHoldoffCount == 0) + +#ifdef USE_INJECTION_POINTS +/* Run injection point when inside smgr API; used by CHECK_FOR_INTERRUPTS */ +#define CHECK_FOR_INTERRUPTS_SMGR_INJECTION() \ + do { \ + if (INTERRUPTS_CAN_BE_PROCESSED() && inside_smgr_api > 0) \ + INJECTION_POINT("SMGR_API", NULL); \ + } while(0) +#else +#define CHECK_FOR_INTERRUPTS_SMGR_INJECTION() ((void)0) +#endif + /* Service interrupt, if one is pending and it's safe to service it now */ #define CHECK_FOR_INTERRUPTS() \ do { \ + CHECK_FOR_INTERRUPTS_SMGR_INJECTION(); \ if (INTERRUPTS_PENDING_CONDITION()) \ ProcessInterrupts(); \ } while(0) -/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */ -#define INTERRUPTS_CAN_BE_PROCESSED() \ - (InterruptHoldoffCount == 0 && CritSectionCount == 0 && \ - QueryCancelHoldoffCount == 0) - #define HOLD_INTERRUPTS() (InterruptHoldoffCount++) #define RESUME_INTERRUPTS() \ diff --git a/src/include/postgres.h b/src/include/postgres.h index 8a41a668687..93145f2d2dc 100644 --- a/src/include/postgres.h +++ b/src/include/postgres.h @@ -536,6 +536,10 @@ Float8GetDatum(float8 X) extern Datum Float8GetDatum(float8 X); #endif +/* Declare for neon use */ +#ifdef USE_INJECTION_POINTS +extern int inside_smgr_api; +#endif /* * Int64GetDatumFast diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 3da0cbc10e0..7b3a851ab18 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -32,6 +32,7 @@ #include "utils/injection_point.h" #include "utils/memutils.h" #include "utils/wait_event.h" +#include PG_MODULE_MAGIC; @@ -61,6 +62,8 @@ typedef struct InjectionPointCondition /* ID of the process where the injection point is allowed to run */ int pid; + /* probability in [0,1], 1.0 = always */ + double prob; } InjectionPointCondition; /* @@ -96,6 +99,8 @@ static InjectionPointSharedState *inj_state = NULL; extern PGDLLEXPORT void injection_error(const char *name, const void *private_data, void *arg); +extern PGDLLEXPORT void injection_error_prob(const char *name, + const void *private_data); extern PGDLLEXPORT void injection_notice(const char *name, const void *private_data, void *arg); @@ -105,6 +110,7 @@ extern PGDLLEXPORT void injection_wait(const char *name, /* track if injection points attached in this process are linked to it */ static bool injection_point_local = false; +static double action2prob(const char *action, int pos); /* * GUC variable @@ -258,7 +264,20 @@ injection_error(const char *name, const void *private_data, void *arg) else elog(ERROR, "error triggered for injection point %s", name); } +void +injection_error_prob(const char *name, const void *private_data) +{ + InjectionPointCondition *condition = (InjectionPointCondition *) private_data; + + if (!injection_point_allowed(condition)) + return; + + /* Use the probability stored in the condition. */ + if ((double) rand() / (double) RAND_MAX > condition->prob) + return; + elog(ERROR, "error triggered for injection point %s", name); +} void injection_notice(const char *name, const void *private_data, void *arg) { @@ -361,6 +380,11 @@ injection_points_attach(PG_FUNCTION_ARGS) function = "injection_notice"; else if (strcmp(action, "wait") == 0) function = "injection_wait"; + else if (strncmp(action, "error-prob-", 11) == 0) + { + condition.prob = action2prob(action, 11); + function = "injection_error_prob"; + } else elog(ERROR, "incorrect action \"%s\" for injection point creation", action); @@ -574,3 +598,29 @@ _PG_init(void) pgstat_register_inj(); pgstat_register_inj_fixed(); } + +/* + * Coverts the action name into probability + */ +static double action2prob(const char *action, const int pos) +{ + /* + * Simple parser: convert "0-01" -> "0.01" then strtod(). + */ + const char *p = action + pos; /* points to "0-01" */ + double prob; + char *endptr; + char buf[32]; + int i, j; + + for (i = 0, j = 0; p[i] != '\0' && j < (int) sizeof(buf) - 1; i++) + { + buf[j++] = (p[i] == '-') ? '.' : p[i]; + } + buf[j] = '\0'; + errno = 0; + prob = strtod(buf, &endptr); + if (errno != 0 || endptr == buf || prob < 0.0 || prob > 1.0) + elog(ERROR, "invalid probability in action \"%s\"", action); + return prob; +}