summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/admin.c89
-rw-r--r--src/bouncer.h2
-rw-r--r--src/janitor.c12
3 files changed, 87 insertions, 16 deletions
diff --git a/src/admin.c b/src/admin.c
index b6822c4..f116192 100644
--- a/src/admin.c
+++ b/src/admin.c
@@ -86,6 +86,34 @@ bool admin_error(PgSocket *admin, const char *fmt, ...)
return res;
}
+static int count_paused_databases(void)
+{
+ List *item;
+ PgDatabase *db;
+ int cnt = 0;
+
+ statlist_for_each(item, &database_list) {
+ db = container_of(item, PgDatabase, head);
+ cnt += db->db_paused;
+ }
+ return cnt;
+}
+
+static int count_db_active(PgDatabase *db)
+{
+ List *item;
+ PgPool *pool;
+ int cnt = 0;
+
+ statlist_for_each(item, &pool_list) {
+ pool = container_of(item, PgPool, head);
+ if (pool->db != db)
+ continue;
+ cnt += pool_server_count(pool);
+ }
+ return cnt;
+}
+
void admin_flush(PgSocket *admin, PktBuf *buf, const char *desc)
{
pktbuf_write_CommandComplete(buf, desc);
@@ -597,20 +625,31 @@ static bool admin_cmd_shutdown(PgSocket *admin, const char *arg)
/* Command: RESUME */
static bool admin_cmd_resume(PgSocket *admin, const char *arg)
{
- int tmp_mode = cf_pause_mode;
if (!admin->admin_user)
return admin_error(admin, "admin access needed");
- log_info("RESUME command issued");
- cf_pause_mode = P_NONE;
- switch (tmp_mode) {
- case P_SUSPEND:
- resume_all();
- case P_PAUSE:
- return admin_ready(admin, "RESUME");
- default:
- return admin_error(admin, "Pooler is not paused/suspended");
+ if (!arg[0]) {
+ int tmp_mode = cf_pause_mode;
+ log_info("RESUME command issued");
+ cf_pause_mode = P_NONE;
+ switch (tmp_mode) {
+ case P_SUSPEND:
+ resume_all();
+ case P_PAUSE:
+ break;
+ default:
+ return admin_error(admin, "Pooler is not paused/suspended");
+ }
+ } else {
+ PgDatabase *db = find_database(arg);
+ log_info("PAUSE '%s' command issued", arg);
+ if (db == NULL)
+ return admin_error(admin, "no such database: %s", arg);
+ if (!db->db_paused)
+ return admin_error(admin, "database %s is not paused", arg);
+ db->db_paused = 0;
}
+ return admin_ready(admin, "RESUME");
}
/* Command: SUSPEND */
@@ -625,6 +664,10 @@ static bool admin_cmd_suspend(PgSocket *admin, const char *arg)
if (cf_pause_mode)
return admin_error(admin, "already suspended/paused");
+ /* suspend needs to be able to flush buffers */
+ if (count_paused_databases() > 0)
+ return admin_error(admin, "cannot suspend with paused databases");
+
log_info("SUSPEND command issued");
cf_pause_mode = P_SUSPEND;
admin->wait_for_response = 1;
@@ -642,9 +685,24 @@ static bool admin_cmd_pause(PgSocket *admin, const char *arg)
if (cf_pause_mode)
return admin_error(admin, "already suspended/paused");
- log_info("PAUSE command issued");
- cf_pause_mode = P_PAUSE;
- admin->wait_for_response = 1;
+ if (!arg[0]) {
+ log_info("PAUSE command issued");
+ cf_pause_mode = P_PAUSE;
+ admin->wait_for_response = 1;
+ } else {
+ PgDatabase *db;
+ log_info("PAUSE '%s' command issued", arg);
+ db = find_database(arg);
+ if (db == NULL)
+ return admin_error(admin, "no such database: %s", arg);
+ if (db == admin->pool->db)
+ return admin_error(admin, "cannot pause admin db: %s", arg);
+ db->db_paused = 1;
+ if (count_db_active(db) > 0)
+ admin->wait_for_response = 1;
+ else
+ return admin_ready(admin, "PAUSE");
+ }
return true;
}
@@ -937,7 +995,10 @@ void admin_pause_done(void)
admin_ready(admin, "SUSPEND");
break;
default:
- fatal("admin_pause_done: bad state");
+ if (count_paused_databases() > 0)
+ admin_ready(admin, "PAUSE");
+ else
+ fatal("admin_pause_done: bad state");
}
admin->wait_for_response = 0;
}
diff --git a/src/bouncer.h b/src/bouncer.h
index f81598a..c586eb0 100644
--- a/src/bouncer.h
+++ b/src/bouncer.h
@@ -182,6 +182,8 @@ struct PgDatabase {
unsigned welcome_msg_len;
unsigned welcome_msg_ready:1;
+ unsigned db_paused:1;
+
/* key/val pairs (without user) for startup msg to be sent to server */
uint8 startup_params[256];
unsigned startup_params_len;
diff --git a/src/janitor.c b/src/janitor.c
index bda506d..a2b5286 100644
--- a/src/janitor.c
+++ b/src/janitor.c
@@ -236,6 +236,7 @@ void per_loop_maint(void)
List *item;
PgPool *pool;
int active = 0;
+ int partial_pause = 0;
/* dont touch anything if takeover is in progress */
if (cf_reboot)
@@ -247,7 +248,11 @@ void per_loop_maint(void)
continue;
switch (cf_pause_mode) {
case P_NONE:
- per_loop_activate(pool);
+ if (pool->db->db_paused) {
+ partial_pause = 1;
+ active += per_loop_pause(pool);
+ } else
+ per_loop_activate(pool);
break;
case P_PAUSE:
active += per_loop_pause(pool);
@@ -264,7 +269,10 @@ void per_loop_maint(void)
case P_PAUSE:
if (!active)
admin_pause_done();
- default:
+ break;
+ case P_NONE:
+ if (partial_pause && !active)
+ admin_pause_done();
break;
}
}