2017-05-31 11:38:06 +00:00
|
|
|
|
|
|
|
/*
|
2018-03-12 14:03:15 +00:00
|
|
|
* Odyssey.
|
2017-05-31 11:38:06 +00:00
|
|
|
*
|
2017-07-05 12:42:49 +00:00
|
|
|
* Advanced PostgreSQL connection pooler.
|
2017-05-31 11:38:06 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2017-05-31 15:47:15 +00:00
|
|
|
#include <inttypes.h>
|
2017-05-31 11:38:06 +00:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <machinarium.h>
|
2017-06-07 11:50:58 +00:00
|
|
|
#include <shapito.h>
|
2017-05-31 11:38:06 +00:00
|
|
|
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/macro.h"
|
|
|
|
#include "sources/version.h"
|
2017-08-08 13:50:50 +00:00
|
|
|
#include "sources/atomic.h"
|
2017-11-27 12:54:16 +00:00
|
|
|
#include "sources/util.h"
|
|
|
|
#include "sources/error.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/list.h"
|
|
|
|
#include "sources/pid.h"
|
|
|
|
#include "sources/id.h"
|
2017-07-26 14:05:29 +00:00
|
|
|
#include "sources/logger.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/daemon.h"
|
2018-03-06 15:23:52 +00:00
|
|
|
#include "sources/config.h"
|
2018-03-05 14:24:30 +00:00
|
|
|
#include "sources/config_reader.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/msg.h"
|
2018-03-13 13:17:27 +00:00
|
|
|
#include "sources/global.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/server.h"
|
|
|
|
#include "sources/server_pool.h"
|
|
|
|
#include "sources/client.h"
|
|
|
|
#include "sources/client_pool.h"
|
|
|
|
#include "sources/route_id.h"
|
|
|
|
#include "sources/route.h"
|
|
|
|
#include "sources/route_pool.h"
|
|
|
|
#include "sources/io.h"
|
2017-09-15 12:58:29 +00:00
|
|
|
#include "sources/instance.h"
|
2018-02-22 13:43:52 +00:00
|
|
|
#include "sources/router_cancel.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/router.h"
|
2018-03-13 13:26:04 +00:00
|
|
|
#include "sources/system.h"
|
2018-03-02 10:00:52 +00:00
|
|
|
#include "sources/worker.h"
|
2017-07-05 12:15:17 +00:00
|
|
|
#include "sources/frontend.h"
|
|
|
|
#include "sources/backend.h"
|
2018-03-02 13:12:32 +00:00
|
|
|
#include "sources/cron.h"
|
2017-05-31 11:38:06 +00:00
|
|
|
|
2017-08-11 10:24:41 +00:00
|
|
|
static inline int
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_stats_server(od_server_t *server, void *arg)
|
2017-08-11 10:24:41 +00:00
|
|
|
{
|
|
|
|
od_serverstat_t *stats = arg;
|
2017-08-11 10:45:11 +00:00
|
|
|
stats->query_time += od_atomic_u64_of(&server->stats.query_time);
|
|
|
|
stats->count_request += od_atomic_u64_of(&server->stats.count_request);
|
2018-04-03 12:03:12 +00:00
|
|
|
stats->count_tx += od_atomic_u64_of(&server->stats.count_tx);
|
2017-08-14 14:19:54 +00:00
|
|
|
stats->recv_client += od_atomic_u64_of(&server->stats.recv_client);
|
|
|
|
stats->recv_server += od_atomic_u64_of(&server->stats.recv_server);
|
2017-08-11 10:24:41 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-31 11:38:06 +00:00
|
|
|
static inline void
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_stats(od_router_t *router)
|
2017-05-31 11:38:06 +00:00
|
|
|
{
|
2018-03-13 13:17:27 +00:00
|
|
|
od_instance_t *instance = router->global->instance;
|
2018-01-09 14:51:58 +00:00
|
|
|
|
2018-01-24 14:28:48 +00:00
|
|
|
if (router->route_pool.count == 0)
|
|
|
|
return;
|
|
|
|
|
2018-03-06 15:23:52 +00:00
|
|
|
if (instance->config.log_stats)
|
2018-02-12 13:29:40 +00:00
|
|
|
{
|
|
|
|
int stream_count = 0;
|
|
|
|
int stream_count_allocated = 0;
|
2018-02-26 12:27:10 +00:00
|
|
|
int stream_total_allocated = 0;
|
2018-02-27 15:38:53 +00:00
|
|
|
int stream_cache_size = 0;
|
2018-02-12 13:29:40 +00:00
|
|
|
shapito_cache_stat(&instance->stream_cache, &stream_count,
|
2018-02-27 15:38:53 +00:00
|
|
|
&stream_count_allocated, &stream_total_allocated,
|
|
|
|
&stream_cache_size);
|
2018-01-09 14:51:58 +00:00
|
|
|
int count_machine = 0;
|
|
|
|
int count_coroutine = 0;
|
|
|
|
int count_coroutine_cache = 0;
|
|
|
|
machinarium_stat(&count_machine, &count_coroutine,
|
|
|
|
&count_coroutine_cache);
|
2018-02-12 13:29:40 +00:00
|
|
|
od_log(&instance->logger, "stats", NULL, NULL,
|
2018-02-27 15:38:53 +00:00
|
|
|
"clients %d, stream cache (%d:%d allocated, %d cached %d bytes), "
|
2018-02-26 12:27:10 +00:00
|
|
|
"coroutines (%d active, %d cached)",
|
|
|
|
router->clients,
|
2018-02-12 13:29:40 +00:00
|
|
|
stream_count_allocated,
|
2018-02-26 12:27:10 +00:00
|
|
|
stream_total_allocated,
|
2018-02-12 13:29:40 +00:00
|
|
|
stream_count,
|
2018-02-27 15:38:53 +00:00
|
|
|
stream_cache_size,
|
2018-02-19 13:18:55 +00:00
|
|
|
count_coroutine,
|
|
|
|
count_coroutine_cache);
|
2018-01-09 14:51:58 +00:00
|
|
|
}
|
|
|
|
|
2017-05-31 11:38:06 +00:00
|
|
|
od_list_t *i;
|
2017-08-11 10:24:41 +00:00
|
|
|
od_list_foreach(&router->route_pool.list, i)
|
|
|
|
{
|
2017-05-31 11:38:06 +00:00
|
|
|
od_route_t *route;
|
|
|
|
route = od_container_of(i, od_route_t, link);
|
2017-08-11 10:24:41 +00:00
|
|
|
|
|
|
|
/* gather statistics per route server pool */
|
|
|
|
od_serverstat_t stats;
|
|
|
|
memset(&stats, 0, sizeof(stats));
|
2017-08-14 14:19:54 +00:00
|
|
|
|
2017-08-11 10:24:41 +00:00
|
|
|
od_serverpool_foreach(&route->server_pool, OD_SACTIVE,
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_stats_server,
|
2017-08-11 10:24:41 +00:00
|
|
|
&stats);
|
|
|
|
od_serverpool_foreach(&route->server_pool, OD_SIDLE,
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_stats_server,
|
2017-08-11 10:24:41 +00:00
|
|
|
&stats);
|
|
|
|
|
2017-08-11 13:54:24 +00:00
|
|
|
/* calculate average between previous sample and the
|
|
|
|
current one */
|
2017-08-14 14:19:54 +00:00
|
|
|
uint64_t recv_client = 0;
|
|
|
|
uint64_t recv_server = 0;
|
2017-08-11 13:54:24 +00:00
|
|
|
uint64_t reqs = 0;
|
2018-04-03 12:03:12 +00:00
|
|
|
uint64_t tps = 0;
|
2017-08-11 15:37:41 +00:00
|
|
|
uint64_t query_time = 0;
|
2017-08-21 10:25:38 +00:00
|
|
|
|
|
|
|
/* ensure server stats not changed due to a
|
|
|
|
* server connection close */
|
|
|
|
int64_t reqs_diff_sanity;
|
2018-03-02 13:12:32 +00:00
|
|
|
reqs_diff_sanity = (stats.count_request - route->cron_stats.count_request);
|
2017-08-21 10:25:38 +00:00
|
|
|
|
|
|
|
if (reqs_diff_sanity >= 0)
|
2017-08-11 13:54:24 +00:00
|
|
|
{
|
2017-08-14 14:19:54 +00:00
|
|
|
/* request count */
|
2017-08-11 13:54:24 +00:00
|
|
|
uint64_t reqs_prev = 0;
|
2018-03-02 13:12:32 +00:00
|
|
|
reqs_prev = route->cron_stats.count_request /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-11 13:54:24 +00:00
|
|
|
|
|
|
|
uint64_t reqs_current = 0;
|
2017-08-11 14:11:44 +00:00
|
|
|
reqs_current = stats.count_request /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-11 13:54:24 +00:00
|
|
|
|
2017-08-21 10:25:38 +00:00
|
|
|
int64_t reqs_diff;
|
|
|
|
reqs_diff = reqs_current - reqs_prev;
|
|
|
|
|
2018-03-06 15:23:52 +00:00
|
|
|
reqs = reqs_diff / instance->config.stats_interval;
|
2017-08-11 13:54:24 +00:00
|
|
|
|
2018-04-03 12:03:12 +00:00
|
|
|
/* transaction count */
|
|
|
|
uint64_t tps_prev = 0;
|
|
|
|
tps_prev = route->cron_stats.count_tx /
|
|
|
|
instance->config.stats_interval;
|
|
|
|
|
|
|
|
uint64_t tps_current = 0;
|
|
|
|
tps_current = stats.count_tx /
|
|
|
|
instance->config.stats_interval;
|
|
|
|
|
|
|
|
int64_t tps_diff;
|
|
|
|
tps_diff = tps_current - tps_prev;
|
|
|
|
|
|
|
|
tps = tps_diff / instance->config.stats_interval;
|
|
|
|
|
2017-08-14 14:19:54 +00:00
|
|
|
/* recv client */
|
|
|
|
uint64_t recv_client_prev = 0;
|
2018-03-02 13:12:32 +00:00
|
|
|
recv_client_prev = route->cron_stats.recv_client /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
uint64_t recv_client_current = 0;
|
|
|
|
recv_client_current = stats.recv_client /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
recv_client = (recv_client_current - recv_client_prev) /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
/* recv server */
|
|
|
|
uint64_t recv_server_prev = 0;
|
2018-03-02 13:12:32 +00:00
|
|
|
recv_server_prev = route->cron_stats.recv_server /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
uint64_t recv_server_current = 0;
|
|
|
|
recv_server_current = stats.recv_server /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
recv_server = (recv_server_current - recv_server_prev) /
|
2018-03-06 15:23:52 +00:00
|
|
|
instance->config.stats_interval;
|
2017-08-14 14:19:54 +00:00
|
|
|
|
|
|
|
/* query time */
|
2017-08-11 13:54:24 +00:00
|
|
|
if (reqs_diff > 0)
|
2018-03-02 13:12:32 +00:00
|
|
|
query_time = (stats.query_time - route->cron_stats.query_time) /
|
2017-08-21 10:25:38 +00:00
|
|
|
reqs_diff;
|
2017-08-11 10:24:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* update stats */
|
2018-03-02 13:12:32 +00:00
|
|
|
route->cron_stats = stats;
|
2017-08-11 10:24:41 +00:00
|
|
|
|
2018-03-02 13:12:32 +00:00
|
|
|
route->cron_stats_avg.count_request = reqs;
|
2018-04-03 12:03:12 +00:00
|
|
|
route->cron_stats_avg.count_tx = tps;
|
2018-03-02 13:12:32 +00:00
|
|
|
route->cron_stats_avg.recv_client = recv_client;
|
|
|
|
route->cron_stats_avg.recv_server = recv_server;
|
|
|
|
route->cron_stats_avg.query_time = query_time;
|
2017-08-11 14:19:32 +00:00
|
|
|
|
2018-03-06 15:23:52 +00:00
|
|
|
if (instance->config.log_stats) {
|
2017-09-21 13:44:19 +00:00
|
|
|
od_log(&instance->logger, "stats", NULL, NULL,
|
2018-03-21 14:36:57 +00:00
|
|
|
"[%.*s.%.*s] clients %d, "
|
2017-08-11 14:11:44 +00:00
|
|
|
"pool_active %d, "
|
|
|
|
"pool_idle %d "
|
|
|
|
"rps %" PRIu64 " "
|
2018-04-03 12:03:12 +00:00
|
|
|
"tps %" PRIu64 " "
|
2017-08-14 14:19:54 +00:00
|
|
|
"query_time_us %" PRIu64 " "
|
|
|
|
"recv_client_bytes %" PRIu64 " "
|
|
|
|
"recv_server_bytes %" PRIu64,
|
2017-08-11 14:11:44 +00:00
|
|
|
route->id.database_len,
|
|
|
|
route->id.database,
|
|
|
|
route->id.user_len,
|
|
|
|
route->id.user,
|
|
|
|
od_clientpool_total(&route->client_pool),
|
|
|
|
route->server_pool.count_active,
|
|
|
|
route->server_pool.count_idle,
|
|
|
|
reqs,
|
2018-04-03 12:03:12 +00:00
|
|
|
tps,
|
2017-08-14 14:19:54 +00:00
|
|
|
query_time,
|
|
|
|
recv_client,
|
|
|
|
recv_server);
|
2017-08-11 14:11:44 +00:00
|
|
|
}
|
2017-05-31 11:38:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_expire_mark(od_server_t *server, void *arg)
|
2017-05-31 11:38:06 +00:00
|
|
|
{
|
|
|
|
od_router_t *router = arg;
|
2018-03-13 13:17:27 +00:00
|
|
|
od_instance_t *instance = router->global->instance;
|
2017-05-31 11:38:06 +00:00
|
|
|
od_route_t *route = server->route;
|
2017-07-18 10:30:23 +00:00
|
|
|
|
|
|
|
/* expire by time-to-live */
|
2018-03-06 15:23:52 +00:00
|
|
|
if (! route->config->pool_ttl)
|
2017-05-31 11:38:06 +00:00
|
|
|
return 0;
|
2017-07-18 10:30:23 +00:00
|
|
|
|
2017-09-21 13:44:19 +00:00
|
|
|
od_debug(&instance->logger, "expire", NULL, server,
|
|
|
|
"idle time: %d",
|
|
|
|
server->idle_time);
|
2018-03-21 14:36:57 +00:00
|
|
|
|
2018-03-06 15:23:52 +00:00
|
|
|
if (server->idle_time < route->config->pool_ttl) {
|
2017-05-31 11:38:06 +00:00
|
|
|
server->idle_time++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
od_serverpool_set(&route->server_pool, server,
|
|
|
|
OD_SEXPIRE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-17 13:45:51 +00:00
|
|
|
static inline void
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_expire(od_cron_t *cron)
|
2017-07-17 13:45:51 +00:00
|
|
|
{
|
2018-03-13 13:17:27 +00:00
|
|
|
od_router_t *router = cron->global->router;
|
|
|
|
od_instance_t *instance = cron->global->instance;
|
2017-07-17 13:45:51 +00:00
|
|
|
|
2017-07-18 10:30:23 +00:00
|
|
|
/* Idle servers expire.
|
|
|
|
*
|
|
|
|
* It is important that mark logic stage must not yield
|
|
|
|
* to maintain iterator consistency.
|
|
|
|
*
|
|
|
|
* mark:
|
2017-07-17 13:45:51 +00:00
|
|
|
*
|
2017-07-18 10:30:23 +00:00
|
|
|
* - If a server idle time is equal to ttl, then move
|
2017-07-17 13:45:51 +00:00
|
|
|
* it to the EXPIRE queue.
|
|
|
|
*
|
2018-03-06 15:23:52 +00:00
|
|
|
* - If a server config marked as obsolete and route has
|
2017-07-19 15:25:44 +00:00
|
|
|
* no remaining clients, then move it to the EXPIRE queue.
|
2017-07-18 10:30:23 +00:00
|
|
|
*
|
|
|
|
* - Add plus one idle second on each traversal.
|
|
|
|
*
|
|
|
|
* sweep:
|
2017-07-17 13:45:51 +00:00
|
|
|
*
|
2017-07-18 10:30:23 +00:00
|
|
|
* - Foreach servers in EXPIRE queue, send Terminate
|
2017-07-17 13:45:51 +00:00
|
|
|
* and close the connection.
|
|
|
|
*/
|
|
|
|
|
2017-07-18 10:30:23 +00:00
|
|
|
/* mark */
|
2017-08-17 15:38:28 +00:00
|
|
|
od_routepool_server_foreach(&router->route_pool, OD_SIDLE,
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_expire_mark,
|
2017-08-17 15:38:28 +00:00
|
|
|
router);
|
2017-07-17 13:45:51 +00:00
|
|
|
|
2017-07-18 10:30:23 +00:00
|
|
|
/* sweep */
|
2017-07-17 13:45:51 +00:00
|
|
|
for (;;) {
|
|
|
|
od_server_t *server;
|
|
|
|
server = od_routepool_next(&router->route_pool, OD_SEXPIRE);
|
|
|
|
if (server == NULL)
|
|
|
|
break;
|
2017-09-21 13:44:19 +00:00
|
|
|
od_debug(&instance->logger, "expire", NULL, server,
|
|
|
|
"closing idle server connection (%d secs)",
|
|
|
|
server->idle_time);
|
2017-07-17 13:45:51 +00:00
|
|
|
server->idle_time = 0;
|
|
|
|
|
|
|
|
od_route_t *route = server->route;
|
|
|
|
server->route = NULL;
|
|
|
|
od_serverpool_set(&route->server_pool, server, OD_SUNDEF);
|
|
|
|
|
2018-02-02 13:53:24 +00:00
|
|
|
if (instance->is_shared)
|
|
|
|
machine_io_attach(server->io);
|
2017-07-17 13:45:51 +00:00
|
|
|
|
2018-02-15 13:06:46 +00:00
|
|
|
od_backend_close_connection(server);
|
2017-07-17 13:45:51 +00:00
|
|
|
od_backend_close(server);
|
|
|
|
}
|
2017-07-19 15:25:44 +00:00
|
|
|
|
2018-03-21 14:36:57 +00:00
|
|
|
/* cleanup unused dynamic routes */
|
2017-07-19 15:25:44 +00:00
|
|
|
od_routepool_gc(&router->route_pool);
|
2017-07-17 13:45:51 +00:00
|
|
|
}
|
|
|
|
|
2017-06-05 14:05:39 +00:00
|
|
|
static void
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron(void *arg)
|
2017-05-31 11:38:06 +00:00
|
|
|
{
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_t *cron = arg;
|
2018-03-13 13:17:27 +00:00
|
|
|
od_router_t *router = cron->global->router;
|
|
|
|
od_instance_t *instance = cron->global->instance;
|
2017-05-31 11:38:06 +00:00
|
|
|
|
2017-08-11 14:11:44 +00:00
|
|
|
int stats_tick = 0;
|
2017-05-31 11:38:06 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
2017-07-17 13:45:51 +00:00
|
|
|
/* mark and sweep expired idle server connections */
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_expire(cron);
|
2017-05-31 11:38:06 +00:00
|
|
|
|
2017-08-11 14:11:44 +00:00
|
|
|
/* update stats */
|
2018-03-06 15:23:52 +00:00
|
|
|
if (++stats_tick >= instance->config.stats_interval) {
|
2018-03-02 13:12:32 +00:00
|
|
|
od_cron_stats(router);
|
2017-08-11 14:11:44 +00:00
|
|
|
stats_tick = 0;
|
2017-05-31 11:38:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 1 second soft interval */
|
|
|
|
machine_sleep(1000);
|
|
|
|
}
|
|
|
|
}
|
2017-06-05 14:05:39 +00:00
|
|
|
|
2018-03-13 13:17:27 +00:00
|
|
|
void od_cron_init(od_cron_t *cron, od_global_t *global)
|
2017-06-05 14:05:39 +00:00
|
|
|
{
|
2018-03-13 13:17:27 +00:00
|
|
|
cron->global = global;
|
2017-06-05 14:05:39 +00:00
|
|
|
}
|
|
|
|
|
2018-03-02 13:12:32 +00:00
|
|
|
int od_cron_start(od_cron_t *cron)
|
2017-06-05 14:05:39 +00:00
|
|
|
{
|
2018-03-13 13:17:27 +00:00
|
|
|
od_instance_t *instance = cron->global->instance;
|
2017-06-05 14:05:39 +00:00
|
|
|
int64_t coroutine_id;
|
2018-03-02 13:12:32 +00:00
|
|
|
coroutine_id = machine_coroutine_create(od_cron, cron);
|
2017-06-05 14:05:39 +00:00
|
|
|
if (coroutine_id == -1) {
|
2018-03-02 13:12:32 +00:00
|
|
|
od_error(&instance->logger, "cron", NULL, NULL,
|
|
|
|
"failed to start cron coroutine");
|
2017-06-19 10:30:56 +00:00
|
|
|
return -1;
|
2017-06-05 14:05:39 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|