pax_global_header00006660000000000000000000000064133177112200014507gustar00rootroot0000000000000052 comment=e7701840806f0666c502e090ee384287fc1a253b pg_rage_terminator-0.1.7/000077500000000000000000000000001331771122000153645ustar00rootroot00000000000000pg_rage_terminator-0.1.7/COPYRIGHT000066400000000000000000000021011331771122000166510ustar00rootroot00000000000000PostgreSQL Database Management System (formerly known as Postgres, then as Postgres95) Copyright (c) 2015, Adrian Vondendriesch Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pg_rage_terminator-0.1.7/Makefile000066400000000000000000000001501331771122000170200ustar00rootroot00000000000000MODULES = pg_rage_terminator PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pg_rage_terminator-0.1.7/README.md000066400000000000000000000021441331771122000166440ustar00rootroot00000000000000# pg_rage_terminator ## About Background worker able to kill random connections based on a configurable chance. It's based on Michael Paquier's background worker 'kill_idle'. The rage backend scan is done using pg_stat_activity. ## Compatible PostgreSQL versions This worker is compatible with PostgreSQL 9.4 and newer versions. ## Installation The installation of pg_rage_terminator is done using the following commands: USE_PGXS=1 make sudo make install After building the background worker you need to change shared_preload_libraries within postgresql.conf: shared_preload_libraries = 'pg_rage_terminator' ## Configuration Following configuration options (GUC) controls the behavior of pg_rage_terminator. * __pg_rage_terminator.chance__: chance to kill a random backend. Valid values are 0 to 100. Where 0 means no backend is killed. 100 ensures every backend is killed. Defaults to 10. * __pg_rage_terminator.interval__: defines the interval of "kill" lookups in seconds. Valid values are 0 to 3600. Where 0 disables the lookup process completely. Defaults to 5 (seconds). pg_rage_terminator-0.1.7/pg_rage_terminator.c000066400000000000000000000202511331771122000214000ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * pg_rage_terminator.c * Kills random connections of a Postgres server. * * Copyright (c) 2015, Adrian Vondendriesch * * IDENTIFICATION * pg_rage_terminator/pg_rage_terminator.c * *------------------------------------------------------------------------- */ /* Some general headers for custom bgworker facility */ #include "postgres.h" #include "fmgr.h" #include "access/xact.h" #include "lib/stringinfo.h" #include "pgstat.h" #include "executor/spi.h" #include "postmaster/bgworker.h" #include "storage/ipc.h" #include "storage/latch.h" #include "storage/proc.h" #include "utils/guc.h" #include "utils/snapmgr.h" /* Allow load of this module in shared libs */ PG_MODULE_MAGIC; /* Entry point of library loading */ void _PG_init(void); /* Signal handling */ static volatile sig_atomic_t got_sigterm = false; static volatile sig_atomic_t got_sighup = false; /* GUC variables */ static int chance = 10; static int interval = 5; /* Worker name */ static char *worker_name = "pg_rage_terminator"; #if PG_VERSION_NUM >= 90500 /* * Forward declaration for main routine. Makes compiler * happy (-Wunused-function, __attribute__((noreturn))) */ void pg_rage_terminator_main(Datum main_arg) pg_attribute_noreturn(); #endif static void pg_rage_terminator_sigterm(SIGNAL_ARGS) { int save_errno = errno; got_sigterm = true; if (MyProc) SetLatch(&MyProc->procLatch); errno = save_errno; } static void pg_rage_terminator_sighup(SIGNAL_ARGS) { int save_errno = errno; got_sighup = true; if (MyProc) SetLatch(&MyProc->procLatch); errno = save_errno; } static void pg_rage_terminator_build_query(StringInfoData *buf) { appendStringInfo(buf, "SELECT " "pid, pg_terminate_backend(pid) as status, " "usename, datname, client_addr::text " "FROM pg_stat_activity " "WHERE client_port IS NOT NULL " "AND ((random() * 100)::int < %d) ", chance); elog(DEBUG1, "Kill query is: %s", buf->data); } void pg_rage_terminator_main(Datum main_arg) { StringInfoData buf; /* Register functions for SIGTERM/SIGHUP management */ pqsignal(SIGHUP, pg_rage_terminator_sighup); pqsignal(SIGTERM, pg_rage_terminator_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* Connect to a database */ #if PG_VERSION_NUM >= 110000 BackgroundWorkerInitializeConnection("postgres", NULL, BGWORKER_SHMEM_ACCESS|BGWORKER_BACKEND_DATABASE_CONNECTION); #else BackgroundWorkerInitializeConnection("postgres", NULL); #endif /* Build query for process */ initStringInfo(&buf); pg_rage_terminator_build_query(&buf); while (!got_sigterm) { int rc = 0; int ret, i; int sleep_interval; if (0 == interval) sleep_interval = 10; else sleep_interval = interval; /* Wait necessary amount of time */ rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleep_interval * 1000L #if PG_VERSION_NUM >= 100000 , PG_WAIT_EXTENSION #endif ); ResetLatch(&MyProc->procLatch); /* Emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); /* Process signals */ if (got_sighup) { int old_chance; /* Save old value of kill chance */ old_chance = chance; /* Process config file */ ProcessConfigFile(PGC_SIGHUP); got_sighup = false; ereport(LOG, (errmsg("bgworker pg_rage_terminator signal: processed SIGHUP"))); /* Rebuild query if necessary */ if (old_chance != chance) { resetStringInfo(&buf); initStringInfo(&buf); pg_rage_terminator_build_query(&buf); } } if (got_sigterm) { /* Simply exit */ ereport(LOG, (errmsg("bgworker pg_rage_terminator signal: processed SIGTERM"))); proc_exit(0); } /* * If interval is 0 we should not do anything. * This has to be done after sighup and sigterm handling. */ if (0 == interval) { elog(LOG, "Nothing to do, sleep zzzzZZZZ"); continue; } /* Process idle connection kill */ SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, buf.data); /* Statement start time */ SetCurrentStatementStartTimestamp(); /* Execute query */ ret = SPI_execute(buf.data, false, 0); /* Some error handling */ if (ret != SPI_OK_SELECT) elog(FATAL, "Error when trying to rage"); /* Do some processing and log stuff disconnected */ for (i = 0; i < SPI_processed; i++) { int32 pidValue; bool isnull; char *datname = NULL; char *usename = NULL; char *client_addr = NULL; /* Fetch values */ pidValue = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull)); usename = DatumGetCString(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 3, &isnull)); datname = DatumGetCString(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 4, &isnull)); client_addr = DatumGetCString(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 5, &isnull)); /* Log what has been disconnected */ elog(LOG, "Rage terminated connection with PID %d %s/%s/%s", pidValue, datname ? datname : "none", usename ? usename : "none", client_addr ? client_addr : "none"); } SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); } /* No problems, so clean exit */ proc_exit(0); } static void pg_rage_terminator_load_params(void) { /* * Kill backends with a chance of . * Look every seconds for new targets. */ DefineCustomIntVariable("pg_rage_terminator.chance", "Chance to terminate a backend in Percent (aboslue).", "Default of 10", &chance, 10, 0, 100, PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomIntVariable("pg_rage_terminator.interval", "Inteval in which pg_rager_terminator looks for new targets (s).", "Default of 5", &interval, 5, -1, 3600, PGC_SIGHUP, 0, NULL, NULL, NULL); } /* * Entry point for worker loading */ void _PG_init(void) { BackgroundWorker worker; /* Add parameters */ pg_rage_terminator_load_params(); /* Worker parameter and registration */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_ConsistentState; /* * bgw_main is considered a footgun, per commit * 2113ac4cbb12 in postgresql.git. Deprecate its * usage here and use the safer method by setting * bgw_function_name and bgw_library_name. PG10 gets * rid of bgw_main completly, but we need to retain * it here to get the initialization correct. */ #if PG_VERSION_NUM < 100000 worker.bgw_main = NULL; #endif snprintf(worker.bgw_library_name, BGW_MAXLEN - 1, "pg_rage_terminator"); snprintf(worker.bgw_function_name, BGW_MAXLEN - 1, "pg_rage_terminator_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "%s", worker_name); /* Wait 10 seconds for restart before crash */ worker.bgw_restart_time = 10; worker.bgw_main_arg = (Datum) 0; #if PG_VERSION_NUM >= 90400 /* * Notify PID is present since 9.4. If this is not initialized * a static background worker cannot start properly. */ worker.bgw_notify_pid = 0; #endif RegisterBackgroundWorker(&worker); }