Hi,
I've written a simple extension that limits number of connection by
IP/db/user, and I do receive this exception:
psql: FATAL: cannot read pg_class without having selected a database
I've found this happens because the extension defines a client auth hook
that reads pg_stat_activity. The really interesting thing is that this
happens only when I start several backends 'at the same time' right
after the cluster is started. From that time, everything works just fine.
So it seems like a race condition or something like that.
I've prepared a simple testcase to demonstrate this issue - see the
files attached. I've put there several 'sleep' to demonstrate the timing
error.
All you need to do is this:
1) compile the extension (make install)
2) add the extension to shared_preload_libraries
3) restart the cluster
4) start two backends at the same time (within a second or so)
Tomas
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include "postgres.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "libpq/auth.h"
#include "pgstat.h"
#include "executor/executor.h"
#include "commands/dbcommands.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
/* allocates space for the rules */
static void pg_limits_shmem_startup(void);
/* check the rules (using pg_stat_activity) */
static void rules_check(Port *port, int status);
/* Saved hook values in case of unload */
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
/* Original Hook */
static ClientAuthentication_hook_type prev_client_auth_hook = NULL;
static LWLockId lock;
void _PG_init(void);
void _PG_fini(void);
/*
* Module load callback
*/
void
_PG_init(void)
{
/* can be preloaded only from postgresql.conf */
if (! process_shared_preload_libraries_in_progress)
elog(ERROR, "connection_limits_shared has to be loaded using "
"shared_preload_libraries");
/*
* Request additional shared resources. (These are no-ops if we're not in
* the postmaster process.) We'll allocate or attach to the shared
* resources in pg_limits_shmem_startup().
*/
RequestAddinLWLocks(1);
/* Install hooks. */
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = pg_limits_shmem_startup;
/* Install Hooks */
prev_client_auth_hook = ClientAuthentication_hook;
ClientAuthentication_hook = rules_check;
}
/*
* Module unload callback
*/
void
_PG_fini(void)
{
/* Uninstall hooks. */
shmem_startup_hook = prev_shmem_startup_hook;
}
/* This is probably the most important part - allocates the shared
* segment, initializes it etc. */
static
void pg_limits_shmem_startup() {
if (prev_shmem_startup_hook)
prev_shmem_startup_hook();
/*
* Create or attach to the shared memory state, including hash table
*/
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
/* First time through ... */
lock = LWLockAssign();
LWLockRelease(AddinShmemInitLock);
}
static
void rules_check(Port *port, int status)
{
int b, nbackends;
PgBackendStatus *beentry;
/*
* Any other plugins which use ClientAuthentication_hook.
*/
if (prev_client_auth_hook)
prev_client_auth_hook(port, status);
/*
* Inject a short delay if authentication failed.
*/
if (status == STATUS_OK)
{
/* lock the segment (serializes the backend creation) */
LWLockAcquire(lock, LW_EXCLUSIVE);
sleep(1);
/* how many backends are already there ? */
nbackends = pgstat_fetch_stat_numbackends();
/* loop through the backends */
for (b = 1; b <= nbackends; b++) {
char * usr, * db;
beentry = pgstat_fetch_stat_beentry(b);
/* pgstatfuncs.c : 630 */
if (beentry != NULL) {
db = get_database_name(beentry->st_databaseid);
usr = GetUserNameFromId(beentry->st_userid);
} /* (beentry != NULL) */
} /* for (b = 1; b <= nbackends; b++) */
}
sleep(4);
LWLockRelease(lock);
}
# issue
comment = '...'
default_version = '1.0.0'
relocatable = true
module_pathname = '$libdir/issue'
MODULE_big = issue
OBJS = issue.o
EXTENSION = issue
MODULES = issue
CFLAGS=`pg_config --includedir-server`
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
all: issue.so
issue.so: issue.o
issue.o : issue.c
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers