mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-17 11:03:36 +08:00
Pass parameters to workers as a struct
Non-functional change. Parameters to workers were passed in as a serialized string, which then needs to be serialized and deserialized using dedicated functions. This commit refactors code to pass parameters to workers as a struct, which is then just copied into the `bgw_extra` field of `BackgroundWorker`. The struct contains simple values and can therefore be copied using memcpy(3c).
This commit is contained in:
parent
5c0110cbbf
commit
533e849c57
@ -56,22 +56,15 @@ typedef enum JobLockLifetime
|
|||||||
} JobLockLifetime;
|
} JobLockLifetime;
|
||||||
|
|
||||||
BackgroundWorkerHandle *
|
BackgroundWorkerHandle *
|
||||||
ts_bgw_job_start(BgwJob *job, Oid user_uid)
|
ts_bgw_job_start(BgwJob *job, Oid user_oid)
|
||||||
{
|
{
|
||||||
int32 job_id = Int32GetDatum(job->fd.id);
|
BgwParams bgw_params = {
|
||||||
StringInfo si = makeStringInfo();
|
.job_id = Int32GetDatum(job->fd.id),
|
||||||
BackgroundWorkerHandle *bgw_handle;
|
.user_oid = user_oid,
|
||||||
|
};
|
||||||
|
strlcpy(bgw_params.bgw_main, job_entrypoint_function_name, sizeof(bgw_params.bgw_main));
|
||||||
|
|
||||||
/* Changing this requires changes to ts_bgw_job_entrypoint */
|
return ts_bgw_start_worker(NameStr(job->fd.application_name), &bgw_params);
|
||||||
appendStringInfo(si, "%u %d", user_uid, job_id);
|
|
||||||
|
|
||||||
bgw_handle = ts_bgw_start_worker(job_entrypoint_function_name,
|
|
||||||
NameStr(job->fd.application_name),
|
|
||||||
si->data);
|
|
||||||
|
|
||||||
pfree(si->data);
|
|
||||||
pfree(si);
|
|
||||||
return bgw_handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BgwJob *
|
static BgwJob *
|
||||||
@ -825,14 +818,13 @@ extern Datum
|
|||||||
ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Oid db_oid = DatumGetObjectId(MyBgworkerEntry->bgw_main_arg);
|
Oid db_oid = DatumGetObjectId(MyBgworkerEntry->bgw_main_arg);
|
||||||
Oid user_uid;
|
BgwParams params;
|
||||||
int32 job_id;
|
|
||||||
BgwJob *job;
|
BgwJob *job;
|
||||||
JobResult res = JOB_FAILURE;
|
JobResult res = JOB_FAILURE;
|
||||||
bool got_lock;
|
bool got_lock;
|
||||||
|
|
||||||
if (sscanf(MyBgworkerEntry->bgw_extra, "%u %d", &user_uid, &job_id) != 2)
|
memcpy(¶ms, MyBgworkerEntry->bgw_extra, sizeof(BgwParams));
|
||||||
elog(ERROR, "job entrypoint got invalid bgw_extra");
|
Assert(params.user_oid != 0 && params.job_id != 0);
|
||||||
|
|
||||||
BackgroundWorkerBlockSignals();
|
BackgroundWorkerBlockSignals();
|
||||||
/* Setup any signal handlers here */
|
/* Setup any signal handlers here */
|
||||||
@ -844,16 +836,14 @@ ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
|||||||
pqsignal(SIGTERM, die);
|
pqsignal(SIGTERM, die);
|
||||||
BackgroundWorkerUnblockSignals();
|
BackgroundWorkerUnblockSignals();
|
||||||
|
|
||||||
elog(DEBUG1, "started background job %d", job_id);
|
BackgroundWorkerInitializeConnectionByOid(db_oid, params.user_oid, 0);
|
||||||
|
|
||||||
BackgroundWorkerInitializeConnectionByOid(db_oid, user_uid, 0);
|
|
||||||
|
|
||||||
ts_license_enable_module_loading();
|
ts_license_enable_module_loading();
|
||||||
|
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
/* Grab a session lock on the job row to prevent concurrent deletes. Lock is released
|
/* Grab a session lock on the job row to prevent concurrent deletes. Lock is released
|
||||||
* when the job process exits */
|
* when the job process exits */
|
||||||
job = ts_bgw_job_find_with_lock(job_id,
|
job = ts_bgw_job_find_with_lock(params.job_id,
|
||||||
TopMemoryContext,
|
TopMemoryContext,
|
||||||
RowShareLock,
|
RowShareLock,
|
||||||
SESSION_LOCK,
|
SESSION_LOCK,
|
||||||
@ -862,7 +852,7 @@ ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
|||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
|
|
||||||
if (job == NULL)
|
if (job == NULL)
|
||||||
elog(ERROR, "job %d not found when running the background worker", job_id);
|
elog(ERROR, "job %d not found when running the background worker", params.job_id);
|
||||||
|
|
||||||
pgstat_report_appname(NameStr(job->fd.application_name));
|
pgstat_report_appname(NameStr(job->fd.application_name));
|
||||||
|
|
||||||
@ -906,7 +896,7 @@ ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
|||||||
* removed the session lock. Don't block and only record if the lock was actually
|
* removed the session lock. Don't block and only record if the lock was actually
|
||||||
* obtained.
|
* obtained.
|
||||||
*/
|
*/
|
||||||
job = ts_bgw_job_find_with_lock(job_id,
|
job = ts_bgw_job_find_with_lock(params.job_id,
|
||||||
TopMemoryContext,
|
TopMemoryContext,
|
||||||
RowShareLock,
|
RowShareLock,
|
||||||
TXN_LOCK,
|
TXN_LOCK,
|
||||||
@ -925,7 +915,7 @@ ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
|||||||
* the rethrow will log the error; but also log which job threw the
|
* the rethrow will log the error; but also log which job threw the
|
||||||
* error
|
* error
|
||||||
*/
|
*/
|
||||||
elog(LOG, "job %d threw an error", job_id);
|
elog(LOG, "job %d threw an error", params.job_id);
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
@ -948,7 +938,10 @@ ts_bgw_job_entrypoint(PG_FUNCTION_ARGS)
|
|||||||
job = NULL;
|
job = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
elog(DEBUG1, "exiting job %d with %s", job_id, (res == JOB_SUCCESS ? "success" : "failure"));
|
elog(DEBUG1,
|
||||||
|
"exiting job %d with %s",
|
||||||
|
params.job_id,
|
||||||
|
(res == JOB_SUCCESS ? "success" : "failure"));
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
#define SCHEDULER_APPNAME "TimescaleDB Background Worker Scheduler"
|
#define SCHEDULER_APPNAME "TimescaleDB Background Worker Scheduler"
|
||||||
#define START_RETRY_MS (1 * INT64CONST(1000)) /* 1 seconds */
|
#define START_RETRY_MS (1 * INT64CONST(1000)) /* 1 seconds */
|
||||||
@ -112,7 +113,7 @@ static void on_failure_to_start_job(ScheduledBgwJob *sjob);
|
|||||||
static volatile sig_atomic_t got_SIGHUP = false;
|
static volatile sig_atomic_t got_SIGHUP = false;
|
||||||
|
|
||||||
BackgroundWorkerHandle *
|
BackgroundWorkerHandle *
|
||||||
ts_bgw_start_worker(const char *function, const char *name, const char *extra)
|
ts_bgw_start_worker(const char *name, const BgwParams *bgw_params)
|
||||||
{
|
{
|
||||||
BackgroundWorker worker = {
|
BackgroundWorker worker = {
|
||||||
.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION,
|
.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION,
|
||||||
@ -125,10 +126,9 @@ ts_bgw_start_worker(const char *function, const char *name, const char *extra)
|
|||||||
|
|
||||||
strlcpy(worker.bgw_name, name, BGW_MAXLEN);
|
strlcpy(worker.bgw_name, name, BGW_MAXLEN);
|
||||||
strlcpy(worker.bgw_library_name, ts_extension_get_so_name(), BGW_MAXLEN);
|
strlcpy(worker.bgw_library_name, ts_extension_get_so_name(), BGW_MAXLEN);
|
||||||
strlcpy(worker.bgw_function_name, function, BGW_MAXLEN);
|
strlcpy(worker.bgw_function_name, bgw_params->bgw_main, sizeof(worker.bgw_function_name));
|
||||||
|
|
||||||
Assert(strlen(extra) < BGW_EXTRALEN);
|
memcpy(worker.bgw_extra, bgw_params, sizeof(*bgw_params));
|
||||||
strlcpy(worker.bgw_extra, extra, BGW_EXTRALEN);
|
|
||||||
|
|
||||||
/* handle needs to be allocated in long-lived memory context */
|
/* handle needs to be allocated in long-lived memory context */
|
||||||
MemoryContextSwitchTo(scheduler_mctx);
|
MemoryContextSwitchTo(scheduler_mctx);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <postmaster/bgworker.h>
|
#include <postmaster/bgworker.h>
|
||||||
|
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
typedef struct ScheduledBgwJob ScheduledBgwJob;
|
typedef struct ScheduledBgwJob ScheduledBgwJob;
|
||||||
|
|
||||||
@ -33,7 +34,6 @@ extern void ts_bgw_job_cache_invalidate_callback(void);
|
|||||||
extern void ts_bgw_scheduler_register_signal_handlers(void);
|
extern void ts_bgw_scheduler_register_signal_handlers(void);
|
||||||
extern void ts_bgw_scheduler_setup_mctx(void);
|
extern void ts_bgw_scheduler_setup_mctx(void);
|
||||||
|
|
||||||
extern BackgroundWorkerHandle *ts_bgw_start_worker(const char *function, const char *name,
|
extern BackgroundWorkerHandle *ts_bgw_start_worker(const char *name, const BgwParams *bgw_params);
|
||||||
const char *extra);
|
|
||||||
|
|
||||||
#endif /* BGW_SCHEDULER_H */
|
#endif /* BGW_SCHEDULER_H */
|
||||||
|
58
src/bgw/worker.h
Normal file
58
src/bgw/worker.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file and its contents are licensed under the Apache License 2.0.
|
||||||
|
* Please see the included NOTICE for copyright information and
|
||||||
|
* LICENSE-APACHE for a copy of the license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BGW_WORKER_H
|
||||||
|
#define BGW_WORKER_H
|
||||||
|
|
||||||
|
#include <postgres.h>
|
||||||
|
|
||||||
|
#include <postmaster/bgworker.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters to background workers.
|
||||||
|
*
|
||||||
|
* Do not add data here that cannot be simply copied to the background worker
|
||||||
|
* using memcpy(3). If it is necessary to add fields that cannot simply be
|
||||||
|
* copied, we need to start using the send and recv functions for the types.
|
||||||
|
*
|
||||||
|
* Only one of `job_id` and `ttl` is passed currently, with `job_id` being used
|
||||||
|
* for normal jobs and `ttl` being used for tests.
|
||||||
|
*
|
||||||
|
* The `bgw_main` is the function to execute when starting the job and is
|
||||||
|
* different depending on whether this is a test runner or the real runner.
|
||||||
|
*
|
||||||
|
* @see ts_bgw_db_scheduler_test_main
|
||||||
|
* @see ts_bgw_job_entrypoint
|
||||||
|
*/
|
||||||
|
typedef struct BgwParams
|
||||||
|
{
|
||||||
|
/** User oid to run the job as. Used when initializing the database
|
||||||
|
* connection. */
|
||||||
|
Oid user_oid;
|
||||||
|
|
||||||
|
/** Job id to use for the worker when executing the job */
|
||||||
|
int32 job_id;
|
||||||
|
|
||||||
|
/** Time to live. Only used in tests. */
|
||||||
|
int32 ttl;
|
||||||
|
|
||||||
|
/** Name of function to call when starting the background worker. */
|
||||||
|
char bgw_main[NAMEDATALEN];
|
||||||
|
} BgwParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile-time check that the size of BgwParams fit into the bgw_extra field
|
||||||
|
* of BackgroundWorker. Relies on the fact that you cannot have arrays with
|
||||||
|
* negative size.
|
||||||
|
*
|
||||||
|
* We cannot use StaticAssertDecl (yet) since it does not exist in PG12 and
|
||||||
|
* not checking this for PG12 could potentially generate hard-to-find
|
||||||
|
* problems.
|
||||||
|
*/
|
||||||
|
static char length_check[sizeof(((BackgroundWorker *) 0)->bgw_extra) -
|
||||||
|
sizeof(BgwParams)] pg_attribute_unused();
|
||||||
|
|
||||||
|
#endif /* BGW_WORKER_H */
|
@ -55,62 +55,11 @@ static const char *test_job_type_names[_MAX_TEST_JOB_TYPE] = {
|
|||||||
[TEST_JOB_TYPE_JOB_4] = "bgw_test_job_4",
|
[TEST_JOB_TYPE_JOB_4] = "bgw_test_job_4",
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *
|
|
||||||
serialize_test_parameters(int32 ttl)
|
|
||||||
{
|
|
||||||
JsonbValue *result;
|
|
||||||
JsonbValue ttl_value;
|
|
||||||
JsonbParseState *parse_state = NULL;
|
|
||||||
Jsonb *jb;
|
|
||||||
StringInfo jtext = makeStringInfo();
|
|
||||||
JsonbValue user_oid;
|
|
||||||
|
|
||||||
user_oid.type = jbvNumeric;
|
|
||||||
user_oid.val.numeric =
|
|
||||||
DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum((int32) GetUserId())));
|
|
||||||
|
|
||||||
ttl_value.type = jbvNumeric;
|
|
||||||
ttl_value.val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(ttl)));
|
|
||||||
|
|
||||||
result = pushJsonbValue(&parse_state, WJB_BEGIN_ARRAY, NULL);
|
|
||||||
|
|
||||||
result = pushJsonbValue(&parse_state, WJB_ELEM, &ttl_value);
|
|
||||||
result = pushJsonbValue(&parse_state, WJB_ELEM, &user_oid);
|
|
||||||
|
|
||||||
result = pushJsonbValue(&parse_state, WJB_END_ARRAY, NULL);
|
|
||||||
|
|
||||||
jb = JsonbValueToJsonb(result);
|
|
||||||
(void) JsonbToCString(jtext, &jb->root, VARSIZE(jb));
|
|
||||||
TestAssertTrue(jtext->len < BGW_EXTRALEN);
|
|
||||||
|
|
||||||
return jtext->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
deserialize_test_parameters(char *params, int32 *ttl, Oid *user_oid)
|
|
||||||
{
|
|
||||||
Jsonb *jb = (Jsonb *) DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(params)));
|
|
||||||
JsonbValue *ttl_v = getIthJsonbValueFromContainer(&jb->root, 0);
|
|
||||||
JsonbValue *user_v = getIthJsonbValueFromContainer(&jb->root, 1);
|
|
||||||
Numeric ttl_numeric;
|
|
||||||
Numeric user_numeric;
|
|
||||||
|
|
||||||
TestAssertTrue(ttl_v->type == jbvNumeric);
|
|
||||||
TestAssertTrue(user_v->type == jbvNumeric);
|
|
||||||
|
|
||||||
ttl_numeric = ttl_v->val.numeric;
|
|
||||||
user_numeric = user_v->val.numeric;
|
|
||||||
*ttl = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(ttl_numeric)));
|
|
||||||
*user_oid =
|
|
||||||
(Oid) DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(user_numeric)));
|
|
||||||
}
|
|
||||||
|
|
||||||
extern Datum
|
extern Datum
|
||||||
ts_bgw_db_scheduler_test_main(PG_FUNCTION_ARGS)
|
ts_bgw_db_scheduler_test_main(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Oid db_oid = DatumGetObjectId(MyBgworkerEntry->bgw_main_arg);
|
Oid db_oid = DatumGetObjectId(MyBgworkerEntry->bgw_main_arg);
|
||||||
int32 ttl;
|
BgwParams bgw_params;
|
||||||
Oid user_oid;
|
|
||||||
|
|
||||||
BackgroundWorkerBlockSignals();
|
BackgroundWorkerBlockSignals();
|
||||||
/* Setup any signal handlers here */
|
/* Setup any signal handlers here */
|
||||||
@ -118,12 +67,12 @@ ts_bgw_db_scheduler_test_main(PG_FUNCTION_ARGS)
|
|||||||
BackgroundWorkerUnblockSignals();
|
BackgroundWorkerUnblockSignals();
|
||||||
ts_bgw_scheduler_setup_callbacks();
|
ts_bgw_scheduler_setup_callbacks();
|
||||||
|
|
||||||
deserialize_test_parameters(MyBgworkerEntry->bgw_extra, &ttl, &user_oid);
|
memcpy(&bgw_params, MyBgworkerEntry->bgw_extra, sizeof(bgw_params));
|
||||||
|
|
||||||
elog(WARNING, "scheduler user id %u", user_oid);
|
elog(WARNING, "scheduler user id %u", bgw_params.user_oid);
|
||||||
elog(WARNING, "running a test in the background: db=%u ttl=%d", db_oid, ttl);
|
elog(WARNING, "running a test in the background: db=%u ttl=%d", db_oid, bgw_params.ttl);
|
||||||
|
|
||||||
BackgroundWorkerInitializeConnectionByOid(db_oid, user_oid, 0);
|
BackgroundWorkerInitializeConnectionByOid(db_oid, bgw_params.user_oid, 0);
|
||||||
|
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
ts_params_get();
|
ts_params_get();
|
||||||
@ -141,33 +90,36 @@ ts_bgw_db_scheduler_test_main(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
ts_bgw_scheduler_setup_mctx();
|
ts_bgw_scheduler_setup_mctx();
|
||||||
|
|
||||||
ts_bgw_scheduler_process(ttl, ts_timer_mock_register_bgw_handle);
|
ts_bgw_scheduler_process(bgw_params.ttl, ts_timer_mock_register_bgw_handle);
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
static BackgroundWorkerHandle *
|
static BackgroundWorkerHandle *
|
||||||
start_test_scheduler(char *params)
|
start_test_scheduler(int32 ttl, Oid user_oid)
|
||||||
{
|
{
|
||||||
|
const BgwParams bgw_params = {
|
||||||
|
.bgw_main = "ts_bgw_db_scheduler_test_main",
|
||||||
|
.ttl = ttl,
|
||||||
|
.user_oid = user_oid,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is where we would increment the number of bgw used, if we
|
* This is where we would increment the number of bgw used, if we
|
||||||
* decide to do so
|
* decide to do so
|
||||||
*/
|
*/
|
||||||
ts_bgw_scheduler_setup_mctx();
|
ts_bgw_scheduler_setup_mctx();
|
||||||
|
|
||||||
return ts_bgw_start_worker("ts_bgw_db_scheduler_test_main",
|
return ts_bgw_start_worker("ts_bgw_db_scheduler_test_main", &bgw_params);
|
||||||
"ts_bgw_db_scheduler_test_main",
|
|
||||||
params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern Datum
|
extern Datum
|
||||||
ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(PG_FUNCTION_ARGS)
|
ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
char *params = serialize_test_parameters(PG_GETARG_INT32(0));
|
|
||||||
BackgroundWorkerHandle *worker_handle;
|
BackgroundWorkerHandle *worker_handle;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
worker_handle = start_test_scheduler(params);
|
worker_handle = start_test_scheduler(PG_GETARG_INT32(0), GetUserId());
|
||||||
|
|
||||||
if (worker_handle != NULL)
|
if (worker_handle != NULL)
|
||||||
{
|
{
|
||||||
@ -190,13 +142,12 @@ static BackgroundWorkerHandle *current_handle = NULL;
|
|||||||
extern Datum
|
extern Datum
|
||||||
ts_bgw_db_scheduler_test_run(PG_FUNCTION_ARGS)
|
ts_bgw_db_scheduler_test_run(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
char *params = serialize_test_parameters(PG_GETARG_INT32(0));
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
MemoryContext old_ctx;
|
MemoryContext old_ctx;
|
||||||
BgwHandleStatus status;
|
BgwHandleStatus status;
|
||||||
|
|
||||||
old_ctx = MemoryContextSwitchTo(TopMemoryContext);
|
old_ctx = MemoryContextSwitchTo(TopMemoryContext);
|
||||||
current_handle = start_test_scheduler(params);
|
current_handle = start_test_scheduler(PG_GETARG_INT32(0), GetUserId());
|
||||||
MemoryContextSwitchTo(old_ctx);
|
MemoryContextSwitchTo(old_ctx);
|
||||||
|
|
||||||
status = WaitForBackgroundWorkerStartup(current_handle, &pid);
|
status = WaitForBackgroundWorkerStartup(current_handle, &pid);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user