agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v1 2/3] Give developer a helpful kick in the pants if they change natts in one place but not another
5+ messages / 3 participants
[nested] [flat]

* [PATCH v1 2/3] Give developer a helpful kick in the pants if they change natts in one place but not another
@ 2020-02-07 14:12  Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Justin Pryzby @ 2020-02-07 14:12 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index bfc629c..d5e1888 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -25,7 +25,9 @@
 #include "access/xlog_internal.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/index.h"
+#include "catalog/pg_class.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "common/link-canary.h"
 #include "libpq/pqsignal.h"
@@ -49,6 +51,7 @@
 #include "utils/ps_status.h"
 #include "utils/rel.h"
 #include "utils/relmapper.h"
+#include "utils/syscache.h"
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
 
@@ -602,6 +605,26 @@ boot_openrel(char *relname)
 	TableScanDesc scan;
 	HeapTuple	tup;
 
+	/* Check that pg_class data is consistent now, rather than failing obscurely later */
+	struct { Oid oid; int natts; }
+		checknatts[] = {
+		{RelationRelationId, Natts_pg_class,},
+		{TypeRelationId, Natts_pg_type,},
+		{AttributeRelationId, Natts_pg_attribute,},
+		{ProcedureRelationId, Natts_pg_proc,},
+	};
+
+	for (int i=0; i<lengthof(checknatts); ++i) {
+		Form_pg_class	classForm;
+		HeapTuple	tuple;
+		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(checknatts[i].oid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for relation %u", checknatts[i].oid);
+		classForm = (Form_pg_class) GETSTRUCT(tuple);
+		Assert(checknatts[i].natts == classForm->relnatts);
+		ReleaseSysCache(tuple);
+	}
+
 	if (strlen(relname) >= NAMEDATALEN)
 		relname[NAMEDATALEN - 1] = '\0';
 
-- 
2.7.4


--uc35eWnScqDcQrv5
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="v1-0003-Make-cluster-a-property-of-table-in-pg_index.patch"



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* [PATCH v1 2/3] Give developer a helpful kick in the pants if they change natts in one place but not another
@ 2020-02-07 14:12  Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Justin Pryzby @ 2020-02-07 14:12 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index bfc629c..d5e1888 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -25,7 +25,9 @@
 #include "access/xlog_internal.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/index.h"
+#include "catalog/pg_class.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "common/link-canary.h"
 #include "libpq/pqsignal.h"
@@ -49,6 +51,7 @@
 #include "utils/ps_status.h"
 #include "utils/rel.h"
 #include "utils/relmapper.h"
+#include "utils/syscache.h"
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
 
@@ -602,6 +605,26 @@ boot_openrel(char *relname)
 	TableScanDesc scan;
 	HeapTuple	tup;
 
+	/* Check that pg_class data is consistent now, rather than failing obscurely later */
+	struct { Oid oid; int natts; }
+		checknatts[] = {
+		{RelationRelationId, Natts_pg_class,},
+		{TypeRelationId, Natts_pg_type,},
+		{AttributeRelationId, Natts_pg_attribute,},
+		{ProcedureRelationId, Natts_pg_proc,},
+	};
+
+	for (int i=0; i<lengthof(checknatts); ++i) {
+		Form_pg_class	classForm;
+		HeapTuple	tuple;
+		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(checknatts[i].oid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for relation %u", checknatts[i].oid);
+		classForm = (Form_pg_class) GETSTRUCT(tuple);
+		Assert(checknatts[i].natts == classForm->relnatts);
+		ReleaseSysCache(tuple);
+	}
+
 	if (strlen(relname) >= NAMEDATALEN)
 		relname[NAMEDATALEN - 1] = '\0';
 
-- 
2.7.4


--TB36FDmn/VVEgNH/--





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* [PATCH v4] Add optional exponential backoff to auth_delay contrib module.
@ 2023-12-27 14:55  Michael Banck <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Michael Banck @ 2023-12-27 14:55 UTC (permalink / raw)

This adds two new GUCs for auth_delay, exponential_backoff and
max_milliseconds. The former controls whether exponential backoff should be
used or not, the latter sets an maximum delay (default is 10s) in case
exponential backoff is active.

The exponential backoff is tracked per remote host and doubled for every failed
login attempt (i.e., wrong password, not just missing pg_hba line or database)
and reset to auth_delay.milliseconds after a successful authentication or when
no authentication attempts have been made for 5*max_milliseconds from that
host.

Authors: Michael Banck, based on an earlier patch by ζˆδΉ‹η„•
Reviewed-by: Abhijit Menon-Sen, Tomas Vondra
Discussion: https://postgr.es/m/AHwAxACqIwIVOEhs5YejpqoG.1.1668569845751.Hmail.zhcheng@ceresdata.com
---
 contrib/auth_delay/auth_delay.c  | 231 ++++++++++++++++++++++++++++++-
 doc/src/sgml/auth-delay.sgml     |  45 +++++-
 src/tools/pgindent/typedefs.list |   1 +
 3 files changed, 273 insertions(+), 4 deletions(-)

diff --git a/contrib/auth_delay/auth_delay.c b/contrib/auth_delay/auth_delay.c
index ff0e1fd461..647aa4e1cd 100644
--- a/contrib/auth_delay/auth_delay.c
+++ b/contrib/auth_delay/auth_delay.c
@@ -14,24 +14,51 @@
 #include <limits.h>
 
 #include "libpq/auth.h"
+#include "miscadmin.h"
 #include "port.h"
+#include "storage/dsm_registry.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
 #include "utils/guc.h"
 #include "utils/timestamp.h"
 
 PG_MODULE_MAGIC;
 
+#define MAX_CONN_RECORDS 100
+
 /* GUC Variables */
 static int	auth_delay_milliseconds = 0;
+static bool auth_delay_exp_backoff = false;
+static int	auth_delay_max_milliseconds = 0;
 
 /* Original Hook */
 static ClientAuthentication_hook_type original_client_auth_hook = NULL;
 
+typedef struct AuthConnRecord
+{
+	char		remote_host[NI_MAXHOST];
+	double		sleep_time;		/* in milliseconds */
+	TimestampTz last_failed_auth;
+} AuthConnRecord;
+
+static shmem_startup_hook_type shmem_startup_next = NULL;
+static AuthConnRecord *acr_array = NULL;
+
+static AuthConnRecord *auth_delay_find_acr_for_host(char *remote_host);
+static AuthConnRecord *auth_delay_find_free_acr(void);
+static double auth_delay_increase_delay_after_failed_conn_auth(Port *port);
+static void auth_delay_cleanup_conn_record(Port *port);
+static void auth_delay_expire_conn_records(Port *port);
+
 /*
  * Check authentication
  */
 static void
 auth_delay_checks(Port *port, int status)
 {
+	double		delay = auth_delay_milliseconds;
+
 	/*
 	 * Any other plugins which use ClientAuthentication_hook.
 	 */
@@ -39,20 +66,193 @@ auth_delay_checks(Port *port, int status)
 		original_client_auth_hook(port, status);
 
 	/*
-	 * Inject a short delay if authentication failed.
+	 * We handle both STATUS_ERROR and STATUS_OK - the third option
+	 * (STATUS_EOF) is disregarded.
+	 *
+	 * In case of STATUS_ERROR we inject a short delay, optionally with
+	 * exponential backoff.
+	 */
+	if (status == STATUS_ERROR)
+	{
+		if (auth_delay_exp_backoff)
+		{
+			/*
+			 * Delay by 2^n seconds after each authentication failure from a
+			 * particular host, where n is the number of consecutive
+			 * authentication failures.
+			 */
+			delay = auth_delay_increase_delay_after_failed_conn_auth(port);
+
+			/*
+			 * Clamp delay to a maximum of auth_delay_max_milliseconds.
+			 */
+			if (auth_delay_max_milliseconds > 0)
+			{
+				delay = Min(delay, auth_delay_max_milliseconds);
+			}
+		}
+
+		if (delay > 0)
+		{
+			elog(DEBUG1, "Authentication delayed for %g seconds due to auth_delay", delay / 1000.0);
+			pg_usleep(1000L * (long) delay);
+		}
+
+		/*
+		 * Expire delays from other hosts after auth_delay_max_milliseconds *
+		 * 5.
+		 */
+		auth_delay_expire_conn_records(port);
+	}
+
+	/*
+	 * Remove host-specific delay if authentication succeeded.
+	 */
+	if (status == STATUS_OK)
+		auth_delay_cleanup_conn_record(port);
+}
+
+static double
+auth_delay_increase_delay_after_failed_conn_auth(Port *port)
+{
+	AuthConnRecord *acr = NULL;
+
+	acr = auth_delay_find_acr_for_host(port->remote_host);
+
+	if (!acr)
+	{
+		acr = auth_delay_find_free_acr();
+
+		if (!acr)
+		{
+			/*
+			 * No free space, MAX_CONN_RECORDS reached. Wait for the
+			 * configured maximum amount.
+			 */
+			elog(LOG, "auth_delay: host connection list full, waiting maximum amount");
+			return auth_delay_max_milliseconds;
+		}
+		strcpy(acr->remote_host, port->remote_host);
+	}
+	if (acr->sleep_time == 0)
+		acr->sleep_time = (double) auth_delay_milliseconds;
+	else
+		acr->sleep_time *= 2;
+
+	/*
+	 * Set current timestamp for later expiry.
 	 */
-	if (status != STATUS_OK)
+	acr->last_failed_auth = GetCurrentTimestamp();
+
+	return acr->sleep_time;
+}
+
+static AuthConnRecord *
+auth_delay_find_acr_for_host(char *remote_host)
+{
+	int			i;
+
+	for (i = 0; i < MAX_CONN_RECORDS; i++)
+	{
+		if (strcmp(acr_array[i].remote_host, remote_host) == 0)
+			return &acr_array[i];
+	}
+
+	return NULL;
+}
+
+static AuthConnRecord *
+auth_delay_find_free_acr(void)
+{
+	int			i;
+
+	for (i = 0; i < MAX_CONN_RECORDS; i++)
+	{
+		if (!acr_array[i].remote_host[0])
+			return &acr_array[i];
+	}
+
+	return 0;
+}
+
+static void
+auth_delay_cleanup_conn_record(Port *port)
+{
+	AuthConnRecord *acr = NULL;
+
+	acr = auth_delay_find_acr_for_host(port->remote_host);
+	if (acr == NULL)
+		return;
+
+	port->remote_host[0] = '\0';
+
+	acr->sleep_time = 0.0;
+	acr->last_failed_auth = 0.0;
+}
+
+static void
+auth_delay_expire_conn_records(Port *port)
+{
+	int			i;
+	TimestampTz now = GetCurrentTimestamp();
+
+	for (i = 0; i < MAX_CONN_RECORDS; i++)
 	{
-		pg_usleep(1000L * auth_delay_milliseconds);
+		/*
+		 * Do not expire the host from which the current authentication
+		 * failure originated.
+		 */
+		if (strcmp(acr_array[i].remote_host, port->remote_host) == 0)
+			continue;
+
+		if (acr_array[i].last_failed_auth > 0 && (long) ((now - acr_array[i].last_failed_auth) / 1000) > 5 * auth_delay_max_milliseconds)
+		{
+			acr_array[i].remote_host[0] = '\0';
+			acr_array[i].sleep_time = 0.0;
+			acr_array[i].last_failed_auth = 0.0;
+		}
 	}
 }
 
+/*
+ * Set up shared memory
+ */
+
+static void
+auth_delay_init_state(void *ptr)
+{
+	Size		shm_size;
+	AuthConnRecord *array = (AuthConnRecord *) ptr;
+
+	shm_size = sizeof(AuthConnRecord) * MAX_CONN_RECORDS;
+
+	memset(array, 0, shm_size);
+}
+
+static void
+auth_delay_shmem_startup(void)
+{
+	bool		found;
+	Size		shm_size;
+
+	if (shmem_startup_next)
+		shmem_startup_next();
+
+	shm_size = sizeof(AuthConnRecord) * MAX_CONN_RECORDS;
+	acr_array = GetNamedDSMSegment("auth_delay", shm_size, auth_delay_init_state, &found);
+}
+
 /*
  * Module Load Callback
  */
 void
 _PG_init(void)
 {
+	if (!process_shared_preload_libraries_in_progress)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("auth_delay must be loaded via shared_preload_libraries")));
+
 	/* Define custom GUC variables */
 	DefineCustomIntVariable("auth_delay.milliseconds",
 							"Milliseconds to delay before reporting authentication failure",
@@ -66,9 +266,34 @@ _PG_init(void)
 							NULL,
 							NULL);
 
+	DefineCustomBoolVariable("auth_delay.exponential_backoff",
+							 "Double the delay after each authentication failure from a particular host",
+							 NULL,
+							 &auth_delay_exp_backoff,
+							 false,
+							 PGC_SIGHUP,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+	DefineCustomIntVariable("auth_delay.max_milliseconds",
+							"Maximum delay when exponential backoff is enabled",
+							NULL,
+							&auth_delay_max_milliseconds,
+							10000,
+							0, INT_MAX / 1000,
+							PGC_SIGHUP,
+							GUC_UNIT_MS,
+							NULL, NULL, NULL);
+
 	MarkGUCPrefixReserved("auth_delay");
 
 	/* Install Hooks */
 	original_client_auth_hook = ClientAuthentication_hook;
 	ClientAuthentication_hook = auth_delay_checks;
+
+	/* Set up shared memory */
+	shmem_startup_next = shmem_startup_hook;
+	shmem_startup_hook = auth_delay_shmem_startup;
 }
diff --git a/doc/src/sgml/auth-delay.sgml b/doc/src/sgml/auth-delay.sgml
index 0571f2a99d..b02b6b0b30 100644
--- a/doc/src/sgml/auth-delay.sgml
+++ b/doc/src/sgml/auth-delay.sgml
@@ -16,6 +16,18 @@
   connection slots.
  </para>
 
+ <para>
+  It is optionally possible to let <filename>auth_delay</filename> wait longer
+  on each successive authentication failure if the configuration parameter
+  <varname>auth_delay.exponential_backoff</varname> is active.  If enabled,
+  <filename>auth_delay</filename> will start with a delay of
+  <varname>auth_delay.milliseconds</varname> and double the delay after each
+  consecutive authentication failure from a particular host, up to the given
+  <varname>auth_delay.max_milliseconds</varname> (default: 10s). If the host
+  authenticates successfully or after a timeout of five times
+  <varname>auth_delay.max_milliseconds</varname>, the delay is reset.
+ </para>
+
  <para>
   In order to function, this module must be loaded via
   <xref linkend="guc-shared-preload-libraries"/> in <filename>postgresql.conf</filename>.
@@ -39,6 +51,35 @@
      </para>
     </listitem>
    </varlistentry>
+   <varlistentry>
+    <term>
+     <varname>auth_delay.exponential_backoff</varname> (<type>bool</type>)
+     <indexterm>
+      <primary><varname>auth_delay.exponential_backoff</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      Whether to use exponential backoff per remote host on authentication
+      failure.  The default is off.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term>
+     <varname>auth_delay.max_milliseconds</varname> (<type>integer</type>)
+     <indexterm>
+      <primary><varname>auth_delay.max_milliseconds</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      The maximum delay, in milliseconds, when exponential backoff is
+      enabled.  The default is 10 seconds.  If set to 0, authentication delays
+      will increase without limit.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
@@ -50,7 +91,9 @@
 # postgresql.conf
 shared_preload_libraries = 'auth_delay'
 
-auth_delay.milliseconds = '500'
+auth_delay.milliseconds = '125'
+auth_delay.exponential_backoff = 'on'
+auth_delay.max_milliseconds = '20000'
 </programlisting>
  </sect2>
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 95ae7845d8..dd9fb9e530 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -166,6 +166,7 @@ AttrMap
 AttrMissing
 AttrNumber
 AttributeOpts
+AuthConnRecord
 AuthRequest
 AuthToken
 AutoPrewarmSharedState
-- 
2.39.2


--iFRdW5/EC4oqxDHL--





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* [PATCH v1 05/20] make use of XLogRecPtrIsInvalid in xlogreader.c
@ 2025-10-28 05:55  Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Bertrand Drouvot @ 2025-10-28 05:55 UTC (permalink / raw)

---
 src/backend/access/transam/xlogreader.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 100.0% src/backend/access/transam/

diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index dcc8d4f9c1b..5e3ebdeacc3 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -558,7 +558,7 @@ XLogDecodeNextRecord(XLogReaderState *state, bool nonblocking)
 
 	RecPtr = state->NextRecPtr;
 
-	if (state->DecodeRecPtr != InvalidXLogRecPtr)
+	if (!XLogRecPtrIsInvalid(state->DecodeRecPtr))
 	{
 		/* read the record after the one we just read */
 
-- 
2.34.1


--GBesP7F6OBOA65K/
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0006-make-use-of-XLogRecPtrIsInvalid-in-xlogrecovery.c.patch"



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* [PATCH v1 06/20] make use of XLogRecPtrIsInvalid in xlogrecovery.c
@ 2025-10-28 05:58  Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Bertrand Drouvot @ 2025-10-28 05:58 UTC (permalink / raw)

---
 src/backend/access/transam/xlogrecovery.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
 100.0% src/backend/access/transam/

diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 3e3c4da01a2..eb999584aa9 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -757,9 +757,9 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 		 * end-of-backup record), and we can enter archive recovery directly.
 		 */
 		if (ArchiveRecoveryRequested &&
-			(ControlFile->minRecoveryPoint != InvalidXLogRecPtr ||
+			(!XLogRecPtrIsInvalid(ControlFile->minRecoveryPoint) ||
 			 ControlFile->backupEndRequired ||
-			 ControlFile->backupEndPoint != InvalidXLogRecPtr ||
+			 !XLogRecPtrIsInvalid(ControlFile->backupEndPoint) ||
 			 ControlFile->state == DB_SHUTDOWNED))
 		{
 			InArchiveRecovery = true;
@@ -3151,7 +3151,7 @@ ReadRecord(XLogPrefetcher *xlogprefetcher, int emode,
 	/* Pass through parameters to XLogPageRead */
 	private->fetching_ckpt = fetching_ckpt;
 	private->emode = emode;
-	private->randAccess = (xlogreader->ReadRecPtr == InvalidXLogRecPtr);
+	private->randAccess = (XLogRecPtrIsInvalid(xlogreader->ReadRecPtr));
 	private->replayTLI = replayTLI;
 
 	/* This is the first attempt to read this page. */
@@ -4336,7 +4336,7 @@ XLogFileReadAnyTLI(XLogSegNo segno, XLogSource source)
 		 * Skip scanning the timeline ID that the logfile segment to read
 		 * doesn't belong to
 		 */
-		if (hent->begin != InvalidXLogRecPtr)
+		if (!XLogRecPtrIsInvalid(hent->begin))
 		{
 			XLogSegNo	beginseg = 0;
 
-- 
2.34.1


--GBesP7F6OBOA65K/
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0007-make-use-of-XLogRecPtrIsInvalid-in-xlogutils.c.patch"



^ permalink  raw  reply  [nested|flat] 5+ messages in thread


end of thread, other threads:[~2025-10-28 05:58 UTC | newest]

Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-02-07 14:12 [PATCH v1 2/3] Give developer a helpful kick in the pants if they change natts in one place but not another Justin Pryzby <[email protected]>
2020-02-07 14:12 [PATCH v1 2/3] Give developer a helpful kick in the pants if they change natts in one place but not another Justin Pryzby <[email protected]>
2023-12-27 14:55 [PATCH v4] Add optional exponential backoff to auth_delay contrib module. Michael Banck <[email protected]>
2025-10-28 05:55 [PATCH v1 05/20] make use of XLogRecPtrIsInvalid in xlogreader.c Bertrand Drouvot <[email protected]>
2025-10-28 05:58 [PATCH v1 06/20] make use of XLogRecPtrIsInvalid in xlogrecovery.c Bertrand Drouvot <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox