--- a/contrib/auth_delay/Makefile
+++ b/contrib/auth_delay/Makefile
@@ -3,6 +3,9 @@
 MODULES = auth_delay
 PGFILEDESC = "auth_delay - delay authentication failure reports"
 
+EXTENSION = auth_delay
+DATA = auth_delay--1.0.sql
+
 ifdef USE_PGXS
 PG_CONFIG = pg_config
 PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/contrib/auth_delay/auth_delay--1.0.sql b/contrib/auth_delay/auth_delay--1.0.sql
new file mode 100644
--- /dev/null
+++ b/contrib/auth_delay/auth_delay--1.0.sql
@@ -0,0 +1,44 @@
+CREATE OR REPLACE FUNCTION auth_delay_conn(OUT id int, OUT remote_host text, OUT delay double precision)
+    RETURNS SETOF record
+    AS 'MODULE_PATHNAME', 'auth_delay_conn'
+    LANGUAGE C IMMUTABLE;
+
+CREATE VIEW auth_delay_conn AS SELECT * FROM auth_delay_conn();
+
+create or replace function release_auth_delay_conn_by_id(id int)
+    returns void
+    AS 'MODULE_PATHNAME', 'release_auth_delay_conn_by_id'
+    LANGUAGE C IMMUTABLE;
+
+create or replace function release_auth_delay_conn_by_name(name cstring)
+    returns void
+    AS 'MODULE_PATHNAME', 'release_auth_delay_conn_by_name'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION auth_delay_user(OUT id int, OUT user_name text, OUT total_attempts int)
+    RETURNS SETOF record
+    AS 'MODULE_PATHNAME', 'auth_delay_user'
+    LANGUAGE C IMMUTABLE;
+
+CREATE VIEW auth_delay_user AS SELECT * FROM auth_delay_user();
+
+create or replace function release_auth_delay_user_by_id(id int)
+    returns void
+    AS 'MODULE_PATHNAME', 'release_auth_delay_user_by_id'
+    LANGUAGE C IMMUTABLE;
+
+create or replace function release_auth_delay_user_by_name(name cstring)
+    returns void
+    AS 'MODULE_PATHNAME', 'release_auth_delay_user_by_name'
+    LANGUAGE C IMMUTABLE;
+
+alter view auth_delay_conn owner to sys;
+alter view auth_delay_user owner to sys;
+revoke execute on function release_auth_delay_conn_by_id(int) from public;
+revoke execute on function release_auth_delay_conn_by_name(cstring) from public;
+revoke execute on function release_auth_delay_user_by_id(int) from public;
+revoke execute on function release_auth_delay_user_by_name(cstring) from public;
+grant execute on function release_auth_delay_conn_by_id(int) to sys;
+grant execute on function release_auth_delay_conn_by_name(cstring) to sys;
+grant execute on function release_auth_delay_user_by_id(int) to sys;
+grant execute on function release_auth_delay_user_by_name(cstring) to sys;
diff --git a/contrib/auth_delay/auth_delay.c b/contrib/auth_delay/auth_delay.c
--- a/contrib/auth_delay/auth_delay.c
+++ b/contrib/auth_delay/auth_delay.c
@@ -2,7 +2,7 @@
  *
  * auth_delay.c
  *
- * Copyright (c) 2010-2019, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
  *		contrib/auth_delay/auth_delay.c
@@ -18,35 +18,711 @@
 #include "utils/guc.h"
 #include "utils/timestamp.h"
 
+#include "access/htup_details.h"
+#include "catalog/pg_type.h"
+#include "lib/ilist.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "utils/builtins.h"
+#include "miscadmin.h"
+#include "pg_config_manual.h"
+#include "funcapi.h"
+
+#include "catalog/pg_authid.h"
+#include "utils/syscache.h"
+
+#include <time.h>
+
 PG_MODULE_MAGIC;
 
 void		_PG_init(void);
 
+#define MAX_CONN_RECORDS 50
+#define MAX_USER_RECORDS 10
+#define SECONDS_IN_HOUR 3600
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
 /* GUC Variables */
 static int	auth_delay_milliseconds;
+static int  auth_delay_max_seconds;
+static double  auth_delay_ratio;
+static int  auth_delay_max_attempts;
+static int  auth_delay_time_range;
+static int  auth_delay_time_interval;
 
 /* Original Hook */
 static ClientAuthentication_hook_type original_client_auth_hook = NULL;
 
+typedef struct AuthConnRecord
+{
+    char    remote_host[NAMEDATALEN];   /* how to decide the length? */
+    bool    used;
+    double  sleep_time;     /* in milliseconds */
+} AuthConnRecord;
+
+typedef struct AuthUserRecord
+{
+    char    user_name[NAMEDATALEN];
+    bool    used;
+    time_t  last_record_time;
+    int     last_record_index;
+    int     fail_slots[FLEXIBLE_ARRAY_MEMBER];
+} AuthUserRecord;
+
+typedef enum AuthStatus
+{
+    AUTH_PASS,
+    AUTH_EQUAL,
+    AUTH_FAIL
+} AuthStatus;
+
+static shmem_startup_hook_type shmem_startup_next = NULL;
+static AuthUserRecord *aur_array = NULL;
+static int *count;
+static AuthConnRecord *acr_array = NULL;
+
+//static bool check_denied_day(Port *port);
+
+static AuthStatus check_auth_rights(Port *port);
+static AuthUserRecord * find_user_record(char *user_name, int *free_index);
+static AuthConnRecord * find_conn_record(char *remote_host, int *free_index);
+static void cleanup_expired_slots(AuthUserRecord *aur);
+
+static double record_failed_conn_auth(Port *port);
+static void record_conn_failure(AuthConnRecord *acr);
+static void record_failed_user_auth(Port *port);
+static void record_user_failure(AuthUserRecord *aur);
+static int get_failure_count(AuthUserRecord *aur);
+static time_t truncated_min_and_sec(const time_t clock);
+
+static void cleanup_conn_record(Port *port);
+static void cleanup_user_record(Port *port);
+
+Datum auth_delay_conn(PG_FUNCTION_ARGS);
+Datum release_auth_delay_conn_by_id(PG_FUNCTION_ARGS);
+Datum release_auth_delay_conn_by_name(PG_FUNCTION_ARGS);
+Datum auth_delay_user(PG_FUNCTION_ARGS);
+Datum release_auth_delay_user_by_id(PG_FUNCTION_ARGS);
+Datum release_auth_delay_user_by_name(PG_FUNCTION_ARGS);
+
 /*
  * Check authentication
  */
 static void
 auth_delay_checks(Port *port, int status)
 {
+//    bool        denied;
+    AuthStatus  auth_status;
+    elog(DEBUG1, "status: %d", status);
+
 	/*
 	 * Any other plugins which use ClientAuthentication_hook.
 	 */
 	if (original_client_auth_hook)
 		original_client_auth_hook(port, status);
 
+//  denied = check_denied_day(port);
+    auth_status = check_auth_rights(port);
+
 	/*
 	 * Inject a short delay if authentication failed.
 	 */
 	if (status != STATUS_OK)
 	{
-		pg_usleep(1000L * auth_delay_milliseconds);
+        double delay;   /* in milliseconds */
+        
+        /* ClientAuthentication() is called twice, ignore the first one
+         * TODO: why? */
+        if (status == STATUS_EOF)
+            return;
+
+        record_failed_user_auth(port);
+        delay = record_failed_conn_auth(port);
+        delay = MIN(delay, 1000L * auth_delay_max_seconds);
+		// pg_usleep(1000L * auth_delay_milliseconds);
+        elog(LOG, "Authentication delayed for %f seconds", delay / 1000.0);
+		pg_usleep(1000L * (long) delay);
+
+//        if (denied)
+//            elog(ERROR, "user \"%s\" is not allowed to login today", port->user_name);
+
+        /* There are two kinks of failure: 1. wrong password 2. current user
+         * is locked. We should not reveal the information that the password
+         * is wrong in the first place. */
+        if (auth_status == AUTH_EQUAL || auth_status == AUTH_FAIL)
+            elog(ERROR, "Too many failed login attempts in the recent");
+	}
+    else if (auth_status == AUTH_FAIL)
+    {
+        /* authentication passed but current user is locked */
+        elog(ERROR, "Too many failed login attempts in the recent");
+    }
+    else
+    {
+        cleanup_conn_record(port);
+        cleanup_user_record(port);
+    }
+    // TODO: success, clean up
+}
+
+/*
+static int
+get_dayofweek()
+{
+    time_t  cur_time;
+    struct tm *p;
+
+    time(&cur_time);
+    p = localtime(&cur_time);
+
+    elog(DEBUG1, "day of week: %d", p->tm_wday);
+    return p->tm_wday;
+}
+*/
+
+/*
+ * Check whether a user is denied to login on current day of week.
+ *     Return: 
+ *        true: login is denied
+ *        false: login is allowed
+ */
+/*
+static bool
+check_denied_day(Port *port)
+{
+    HeapTuple   tuple;
+    Datum       datum;
+    bool        isnull;
+    char       *denied_day;
+    int         i;
+    int         dayofweek;
+    bool        result;
+    
+    tuple = SearchSysCache1(AUTHNAME,
+                            PointerGetDatum(port->user_name));
+    if (!HeapTupleIsValid(tuple))
+	{
+		elog(ERROR, "Role \"%s\" does not exist.", port->user_name);
 	}
+
+    datum = SysCacheGetAttr(AUTHNAME, tuple, Anum_pg_authid_roldeniedday, &isnull);
+    if (isnull)
+    {
+        result = false;
+    }
+    else
+    {
+        denied_day = text_to_cstring(DatumGetTextP(datum));
+
+        dayofweek = get_dayofweek();
+
+        result =  false;
+        for (i = 0; i < strlen(denied_day); i++)
+        {
+            if (denied_day[i] == ',')
+                continue;
+            if (denied_day[i] == ('0' + dayofweek))
+                result =  true;
+        }
+    }
+
+	ReleaseSysCache(tuple);
+    
+    return result;
+}
+*/
+
+/*
+ * Get AuthUserRecord pointer according to given index
+ * Since the length of AuthUserRecord is not fixed, we have to calculate
+ * the offset.
+ */
+static AuthUserRecord *
+get_aur(int index)
+{
+    int length;
+
+    Assert(index < MAX_USER_RECORDS);
+    length = sizeof(AuthUserRecord) + sizeof(int) * (auth_delay_time_range /
+                auth_delay_time_interval - 1);
+
+    return (AuthUserRecord *) ((char *)aur_array + length * index);
+}
+
+/*
+ * Check whether current user has the right to login
+ */
+static AuthStatus
+check_auth_rights(Port *port)
+{
+    int             free_index;
+    AuthUserRecord *aur = NULL;
+
+    aur = find_user_record(port->user_name, &free_index);
+    if (aur == NULL)
+        return AUTH_PASS;
+
+    cleanup_expired_slots(aur);
+    if (get_failure_count(aur) > auth_delay_max_attempts)
+        return AUTH_FAIL;
+    else if (get_failure_count(aur) == auth_delay_max_attempts)
+        return AUTH_EQUAL;
+    else
+        return AUTH_PASS;
+}
+
+static AuthUserRecord *
+find_user_record(char *user_name, int *free_index)
+{
+    int     i;
+
+    *free_index = -1;
+    for (i = 0; i < MAX_USER_RECORDS; i++)
+    {
+        if (!get_aur(i)->used)
+        {
+            if (*free_index == -1)
+                *free_index = i;// record unused element
+            continue;
+        }
+        if (strcmp(get_aur(i)->user_name, user_name) == 0)
+            return get_aur(i);
+    }
+
+    return NULL;
+}
+
+static AuthConnRecord *
+find_conn_record(char *remote_host, int *free_index)
+{
+    int     i;
+
+    *free_index = -1;
+    for (i = 0; i < MAX_CONN_RECORDS; i++)
+    {
+        if (!acr_array[i].used)
+        {
+            if (*free_index == -1)
+                *free_index = i;// record unused element
+            continue;
+        }
+        if (strcmp(acr_array[i].remote_host, remote_host) == 0)
+            return &acr_array[i];
+    }
+
+    return NULL;
+}
+
+static void
+cleanup_expired_slots(AuthUserRecord *aur)
+{
+    time_t  cur_time;
+    int     i;
+    int     index;
+    int     interval;
+    int     num_slots;
+
+    time(&cur_time);
+    cur_time = truncated_min_and_sec(cur_time);
+
+    interval = (cur_time - aur->last_record_time) / SECONDS_IN_HOUR;
+    interval = MIN(interval, auth_delay_time_range) / auth_delay_time_interval;
+    elog(DEBUG1, "interval: %d", interval);
+    num_slots = auth_delay_time_range / auth_delay_time_interval;
+
+    index = aur->last_record_index;// in case interval = 0
+    for (i = 1; i <= interval; i++)
+    {
+        /* These slots are out of time range */
+        index = (aur->last_record_index + i) % num_slots;
+        aur->fail_slots[index] = 0;
+    }
+    aur->last_record_index = index;
+    // aur->last_record_time = cur_time;
+}
+
+static double
+record_failed_conn_auth(Port *port)
+{
+    AuthConnRecord *acr = NULL;
+    int         j = -1;
+
+    acr = find_conn_record(port->remote_host, &j);
+
+    if (!acr)
+    {
+        Assert(j != -1);// XXX: no free space, MAX_CONN_RECORDS too small
+        acr = &acr_array[j];
+        strcpy(acr->remote_host, port->remote_host);
+        acr->used = true;
+        elog(DEBUG1, "new connection: %s, index: %d", acr->remote_host, j);
+    }
+
+    record_conn_failure(acr);
+    return acr->sleep_time;
+}
+
+static void
+record_conn_failure(AuthConnRecord *acr)
+{
+    if (acr->sleep_time == 0)
+        acr->sleep_time = (double) auth_delay_milliseconds;
+    else
+        acr->sleep_time *= auth_delay_ratio;
+    elog(DEBUG1, "connection %s will sleep for %f seconds",
+            acr->remote_host, acr->sleep_time / 1000.0);
+}
+
+static void
+record_failed_user_auth(Port *port)
+{
+    AuthUserRecord *aur = NULL;
+    int         j = -1;
+
+    (*count)++;
+    elog(DEBUG1, "count: %d", *count);
+
+    aur = find_user_record(port->user_name, &j);
+    elog(DEBUG1, "record_failed_user_auth: %d, %d", get_aur(0)->fail_slots[0],
+            get_aur(0)->fail_slots[1]);
+
+    if (!aur)
+    {
+        Assert(j != -1);// XXX: no free space, MAX_USER_RECORDS too small
+        aur = get_aur(j);
+        strcpy(aur->user_name, port->user_name);
+        aur->used = true;
+        elog(DEBUG1, "new user: %s, index: %d", aur->user_name, j);
+    }
+
+    record_user_failure(aur);
+}
+
+static time_t
+truncated_min_and_sec(const time_t clock)
+{
+    struct tm *p = localtime(&clock);
+
+    p->tm_min = 0;
+    p->tm_sec = 0;
+
+    return mktime(p);
+}
+
+static void
+record_user_failure(AuthUserRecord *aur)
+{
+    int     index = -1;
+    time_t  cur_time;
+
+    if (aur->last_record_time == 0)
+    {
+        /* New record */
+        aur->fail_slots[0] = 1;
+        aur->last_record_index = 0;
+    }
+    else
+    {
+        index = aur->last_record_index;
+        aur->fail_slots[index]++;
+    }
+
+    time(&cur_time);
+    cur_time = truncated_min_and_sec(cur_time);
+    aur->last_record_time = cur_time;
+    elog(DEBUG1, "user: %s, failure count: %d, index: %d",
+            aur->user_name, get_failure_count(aur), index);
+    elog(DEBUG1, "record_user_failure: %d, %d", get_aur(0)->fail_slots[0],
+            get_aur(0)->fail_slots[1]);
+}
+
+static int
+get_failure_count(AuthUserRecord *aur)
+{
+    int i;
+    int count = 0;
+    for (i = 0; i < auth_delay_time_range / auth_delay_time_interval; i++)
+        count += aur->fail_slots[i];
+
+    elog(DEBUG1, "total count: %d", count);
+    return count;
+}
+
+static void
+cleanup_conn_record(Port *port)
+{
+    int             free_index;
+    AuthConnRecord *acr = NULL;
+
+    acr = find_conn_record(port->remote_host, &free_index);
+    if (acr == NULL)
+        return;
+
+    acr->used = false;
+    acr->sleep_time = 0.0;
+}
+
+static void
+cleanup_user_record(Port *port)
+{
+    int             free_index;
+    AuthUserRecord *aur = NULL;
+
+    aur = find_user_record(port->user_name, &free_index);
+    if (aur == NULL)
+        return;
+
+    aur->used = false;
+    aur->last_record_time = 0;
+    aur->last_record_index = 0;
+    memset(aur->fail_slots, 0, auth_delay_time_range/auth_delay_time_interval);
+}
+
+Datum
+auth_delay_conn(PG_FUNCTION_ARGS)
+{
+    FuncCallContext *funcctx;
+    Datum            values[3];
+    bool             nulls[3];
+
+    elog(DEBUG1, "auth_delay_conn() is called");
+    if (SRF_IS_FIRSTCALL())
+    {
+        MemoryContext   oldcontext;
+        TupleDesc       tupdesc;
+        
+        funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+/*
+ * Modified: CreateTemplateTupleDesc(int, bool) -> CreateTemplateTupleDesc(int)
+ */
+        tupdesc = CreateTemplateTupleDesc(3);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "id",
+						   INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "remote_host",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "delay",
+						   FLOAT8OID, -1, 0);
+        funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+
+        funcctx->max_calls = MAX_CONN_RECORDS;
+
+        MemoryContextSwitchTo(oldcontext);
+    }
+
+    funcctx = SRF_PERCALL_SETUP();
+
+    while (funcctx->call_cntr < funcctx->max_calls)
+    {
+        HeapTuple       tuple;
+        Datum           result;
+        AuthConnRecord *acr;
+        
+        acr = &acr_array[funcctx->call_cntr];
+        elog(DEBUG1, "cntr: %ld, remote host: %s, used: %d", funcctx->call_cntr, acr->remote_host, acr->used);
+        if (!acr->used)
+        {
+            funcctx->call_cntr++;
+            continue;
+        }
+
+        elog(DEBUG1, "remote host: %s", acr->remote_host);
+        memset(nulls, 0, sizeof(nulls));
+        values[0] = Int32GetDatum(funcctx->call_cntr);
+        values[1] = CStringGetTextDatum(acr->remote_host);
+        values[2] = Float8GetDatum(acr->sleep_time / 1000.0);
+
+        tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+        result = HeapTupleGetDatum(tuple);
+
+        SRF_RETURN_NEXT(funcctx, result);
+    }
+    SRF_RETURN_DONE(funcctx);
+}
+PG_FUNCTION_INFO_V1(auth_delay_conn);
+
+Datum
+release_auth_delay_conn_by_id(PG_FUNCTION_ARGS)
+{
+    int     id = PG_GETARG_DATUM(0);
+
+    if (id >= MAX_CONN_RECORDS || !acr_array[id].used)
+        elog(ERROR, "Invalid id");
+    else
+    {
+        acr_array[id].used = false;
+        acr_array[id].sleep_time = 0.0;
+    }
+    PG_RETURN_VOID();
+}
+PG_FUNCTION_INFO_V1(release_auth_delay_conn_by_id);
+
+Datum
+release_auth_delay_conn_by_name(PG_FUNCTION_ARGS)
+{
+    int     j;
+    char   *remote_host = PG_GETARG_CSTRING(0);
+    AuthConnRecord *acr = find_conn_record(remote_host, &j);
+
+    if (!acr)
+        elog(ERROR, "Invalid remote host");
+    else
+    {
+        acr->used = false;
+        acr->sleep_time = 0.0;
+    }
+
+    PG_RETURN_VOID();
+}
+PG_FUNCTION_INFO_V1(release_auth_delay_conn_by_name);
+
+Datum
+auth_delay_user(PG_FUNCTION_ARGS)
+{
+    FuncCallContext *funcctx;
+    Datum            values[3];
+    bool             nulls[3];
+
+    if (SRF_IS_FIRSTCALL())
+    {
+        MemoryContext   oldcontext;
+        TupleDesc       tupdesc;
+        
+        funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+/*
+ * Modified: CreateTemplateTupleDesc(int, bool) -> CreateTemplateTupleDesc(int)
+ */
+        tupdesc = CreateTemplateTupleDesc(3);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "id",
+						   INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "user_name",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "total_attempts",
+						   INT4OID, -1, 0);
+        funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+
+        funcctx->max_calls = MAX_USER_RECORDS;
+
+        MemoryContextSwitchTo(oldcontext);
+    }
+
+    funcctx = SRF_PERCALL_SETUP();
+
+    while (funcctx->call_cntr < funcctx->max_calls)
+    {
+        HeapTuple       tuple;
+        Datum           result;
+        AuthUserRecord *aur;
+        
+        aur = get_aur(funcctx->call_cntr);
+        if (!aur->used)
+        {
+            funcctx->call_cntr++;
+            continue;
+        }
+
+        elog(DEBUG1, "fail_slots: %d, %d", aur->fail_slots[0], aur->fail_slots[1]);
+        memset(nulls, 0, sizeof(nulls));
+        values[0] = Int32GetDatum(funcctx->call_cntr);
+        values[1] = CStringGetTextDatum(aur->user_name);
+        values[2] = Int32GetDatum(get_failure_count(aur));
+
+        tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+        result = HeapTupleGetDatum(tuple);
+
+        SRF_RETURN_NEXT(funcctx, result);
+    }
+    SRF_RETURN_DONE(funcctx);
+}
+PG_FUNCTION_INFO_V1(auth_delay_user);
+
+Datum
+release_auth_delay_user_by_id(PG_FUNCTION_ARGS)
+{
+    int     id = PG_GETARG_DATUM(0);
+    AuthUserRecord *aur;
+
+    if (id >= MAX_USER_RECORDS || !get_aur(id)->used)
+        elog(ERROR, "Invalid id");
+    else
+    {
+        aur = get_aur(id);
+        aur->used = false;
+        aur->last_record_time = 0;
+        aur->last_record_index = 0;
+        memset(aur->fail_slots, 0, auth_delay_time_range/auth_delay_time_interval);
+    }
+    PG_RETURN_VOID();
+}
+PG_FUNCTION_INFO_V1(release_auth_delay_user_by_id);
+
+Datum
+release_auth_delay_user_by_name(PG_FUNCTION_ARGS)
+{
+    int     j;
+    char   *user_name = PG_GETARG_CSTRING(0);
+    AuthUserRecord *aur = find_user_record(user_name, &j);
+
+    if (!aur)
+        elog(ERROR, "Invalid user name");
+    else
+    {
+        aur->used = false;
+        aur->last_record_time = 0;
+        aur->last_record_index = 0;
+        memset(aur->fail_slots, 0, auth_delay_time_range/auth_delay_time_interval);
+    }
+    PG_RETURN_VOID();
+}
+PG_FUNCTION_INFO_V1(release_auth_delay_user_by_name);
+
+/*
+ * Set up shared memory
+ */
+static void
+auth_delay_shmem_startup(void)
+{
+    Size    required;
+    bool    found;
+    
+    if (shmem_startup_next)
+        shmem_startup_next();
+
+    count = ShmemInitStruct("Count of failure", sizeof(int), &found);
+    if (found)
+        elog(DEBUG1, "variable count already exists: %d", *count);
+    *count = 0;
+
+    required = (sizeof(AuthUserRecord) + sizeof(int) * (auth_delay_time_range /
+                auth_delay_time_interval - 1)) * MAX_USER_RECORDS;
+    aur_array = ShmemInitStruct("Array of AuthUserRecord", required, &found);
+    if (found)// this should not happen?
+        elog(DEBUG1, "variable aur_array already exists");
+    /* all fileds are set to 0 */
+    memset(aur_array, 0, required);
+
+    required = sizeof(AuthConnRecord) * MAX_CONN_RECORDS;
+    acr_array = ShmemInitStruct("Array of AuthConnRecord", required, &found);
+    if (found)// this should not happen?
+        elog(DEBUG1, "variable acr_array already exists");
+    /* all fileds are set to 0 */
+    memset(acr_array, 0, required);
+}
+
+static bool
+guc_check_time_interval(int *newval, void **extra, GucSource source)
+{
+    if (*newval > auth_delay_time_range)
+        elog(ERROR, "auth_delay.time_interval should not be greater than "
+                "auth_delay.time_range");
+    if (auth_delay_time_range % *newval != 0)
+        elog(ERROR, "auth_delay.time_range should be integer multiple of "
+                "auth_delay.time_interval");
+
+    return true;
 }
 
 /*
@@ -55,6 +731,13 @@ auth_delay_checks(Port *port, int status)
 void
 _PG_init(void)
 {
+    Size    required;
+
+    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",
@@ -64,10 +747,64 @@ _PG_init(void)
 							0, INT_MAX / 1000,
 							PGC_SIGHUP,
 							GUC_UNIT_MS,
+							NULL, NULL, NULL);
+	DefineCustomIntVariable("auth_delay.max_seconds",
+							"Maximum seconds to wait if fail to login",
 							NULL,
+							&auth_delay_max_seconds,
+							100,
+							0, INT_MAX,
+							PGC_POSTMASTER,
+							GUC_UNIT_S,
+							NULL, NULL, NULL);
+	DefineCustomRealVariable("auth_delay.ratio",
+							"Incremental ratio of waiting time",
+							NULL,
+							&auth_delay_ratio,
+							1.0,
+							1.0, 1000.0,
+							PGC_POSTMASTER,
+							GUC_UNIT_S,
+							NULL, NULL, NULL);
+	DefineCustomIntVariable("auth_delay.max_attempts",
+							"Maximum number of attempts to login of a user",
+							NULL,
+							&auth_delay_max_attempts,
+							2,
+							0, 1000,
+							PGC_POSTMASTER,
+							0,
+							NULL, NULL, NULL);
+	DefineCustomIntVariable("auth_delay.time_range",
+							"Time range during which failure login attempts are counted",
 							NULL,
-							NULL);
+							&auth_delay_time_range,
+							1,
+							1, 100,
+							PGC_POSTMASTER,
+							0,
+							NULL, NULL, NULL);
+	DefineCustomIntVariable("auth_delay.time_interval",
+							"Time interval during which failure login attempts are counted",
+							NULL,
+							&auth_delay_time_interval,
+							1,
+							1, 100,
+							PGC_POSTMASTER,
+							0,
+                            guc_check_time_interval,
+							NULL, NULL);
 	/* 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;
+
+    required = (sizeof(AuthUserRecord) + sizeof(int) * (auth_delay_time_range /
+                auth_delay_time_interval - 1)) * MAX_USER_RECORDS;
+    required += sizeof(AuthConnRecord) * MAX_CONN_RECORDS;
+    required += sizeof(int);
+    RequestAddinShmemSpace(required);
 }
diff --git a/contrib/auth_delay/auth_delay.control b/contrib/auth_delay/auth_delay.control
new file mode 100644
--- /dev/null
+++ b/contrib/auth_delay/auth_delay.control
@@ -0,0 +1,5 @@
+# auth_delay extension
+comment = 'Extented authentication delay for users and connections'
+default_version = '1.0'
+module_pathname = '$libdir/auth_delay'
+relocatable = false
