pax_global_header00006660000000000000000000000064125157155420014521gustar00rootroot0000000000000052 comment=3086531929af4264fbc1a35c7c44b8de4a4d99b9 pg_rage_terminator-0.1.4/000077500000000000000000000000001251571554200153735ustar00rootroot00000000000000pg_rage_terminator-0.1.4/COPYRIGHT000066400000000000000000000021011251571554200166600ustar00rootroot00000000000000PostgreSQL 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.4/Makefile000066400000000000000000000001501251571554200170270ustar00rootroot00000000000000MODULES = pg_rage_terminator PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pg_rage_terminator-0.1.4/README.md000066400000000000000000000020411251571554200166470ustar00rootroot00000000000000# 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'. Rage backend scan is done using pg_stat_activity. ## Compatible PostgreSQL versions This worker is compatible with PostgreSQL 9.3 and newer versions. ## Installation 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 change shared_preload_libraries within postgresql.conf: shared_preload_libraries = 'pg_rage_terminator' ## Configuration Following configuration options (GUC) controls pg_rage_terminator. * __pg_rage_terminator.chance__: chance to kill a random backend. Valid values are 0 to 100. Where 0 means no backends is killed. 100 ensures every backend is killed. * __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. pg_rage_terminator-0.1.4/pg_rage_terminator.c000066400000000000000000000164061251571554200214160ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * 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"; 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); } static 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 */ BackgroundWorkerInitializeConnection("postgres", NULL); /* 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); 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; worker.bgw_main = 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); }