return admin_ready(admin, "KILL");
}
+/* Command: WAIT_CLOSE */
+static bool admin_cmd_wait_close(PgSocket *admin, const char *arg)
+{
+ if (!admin->admin_user)
+ return admin_error(admin, "admin access needed");
+
+ if (!arg[0]) {
+ struct List *item;
+ PgPool *pool;
+ int active = 0;
+
+ log_info("WAIT_CLOSE command issued");
+ statlist_for_each(item, &pool_list) {
+ PgDatabase *db;
+
+ pool = container_of(item, PgPool, head);
+ db = pool->db;
+ db->db_wait_close = 1;
+ active += count_db_active(db);
+ }
+ if (active > 0)
+ admin->wait_for_response = 1;
+ else
+ return admin_ready(admin, "WAIT_CLOSE");
+ } else {
+ PgDatabase *db;
+
+ log_info("WAIT_CLOSE '%s' command issued", arg);
+ db = find_or_register_database(admin, arg);
+ if (db == NULL)
+ return admin_error(admin, "no such database: %s", arg);
+ if (db == admin->pool->db)
+ return admin_error(admin, "cannot wait in admin db: %s", arg);
+ db->db_wait_close = 1;
+ if (count_db_active(db) > 0)
+ admin->wait_for_response = 1;
+ else
+ return admin_ready(admin, "WAIT_CLOSE");
+ }
+
+ return true;
+}
+
/* extract substring from regex group */
static bool copy_arg(const char *src, regmatch_t *glist,
int gnum, char *dst, unsigned dstmax,
"\tRECONNECT [<db>]\n"
"\tKILL <db>\n"
"\tSUSPEND\n"
- "\tSHUTDOWN", "");
+ "\tSHUTDOWN\n",
+ "\tWAIT_CLOSE [<db>]", "");
if (res)
res = admin_ready(admin, "SHOW");
return res;
{"show", admin_cmd_show},
{"shutdown", admin_cmd_shutdown},
{"suspend", admin_cmd_suspend},
+ {"wait_close", admin_cmd_wait_close},
{NULL, NULL}
};
}
}
+void admin_wait_close_done(void)
+{
+ struct List *item, *tmp;
+ PgSocket *admin;
+ bool res;
+
+ statlist_for_each_safe(item, &admin_pool->active_client_list, tmp) {
+ admin = container_of(item, PgSocket, head);
+ if (!admin->wait_for_response)
+ continue;
+
+ res = admin_ready(admin, "WAIT_CLOSE");
+
+ if (!res)
+ disconnect_client(admin, false, "dead admin");
+ else
+ admin->wait_for_response = 0;
+ }
+}
+
/* admin on console has pressed ^C */
void admin_handle_cancel(PgSocket *admin)
{
return active;
}
+/*
+ * Count the servers in server_list that have close_needed set.
+ */
+static int count_close_needed(struct StatList *server_list)
+{
+ struct List *item;
+ PgSocket *server;
+ int count = 0;
+
+ statlist_for_each(item, server_list) {
+ server = container_of(item, PgSocket, head);
+ if (server->close_needed)
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Per-loop tasks for WAIT_CLOSE
+ */
+static int per_loop_wait_close(PgPool *pool)
+{
+ int count = 0;
+
+ if (pool->db->admin)
+ return 0;
+
+ count += count_close_needed(&pool->active_server_list);
+ count += count_close_needed(&pool->idle_server_list);
+ count += count_close_needed(&pool->new_server_list);
+ count += count_close_needed(&pool->tested_server_list);
+ count += count_close_needed(&pool->used_server_list);
+
+ return count;
+}
+
/*
* this function is called for each event loop.
*/
struct List *item;
PgPool *pool;
int active = 0;
+ int waiting_count = 0;
int partial_pause = 0;
+ int partial_wait = 0;
bool force_suspend = false;
if (cf_pause_mode == P_SUSPEND && cf_suspend_timeout > 0) {
active += per_loop_suspend(pool, force_suspend);
break;
}
+
+ if (pool->db->db_wait_close) {
+ partial_wait = 1;
+ waiting_count += per_loop_wait_close(pool);
+ }
}
switch (cf_pause_mode) {
admin_pause_done();
break;
}
+
+ if (partial_wait && !waiting_count)
+ admin_wait_close_done();
}
/* maintaining clients in pool */