diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index c8fccdaa1..41aa17a24 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -438,6 +438,7 @@ rewrite_rule|enum|lazyagg,magicset,partialpush,uniquecheck,disablerep,intargetli search_path|string|0,0|NULL|NULL| session_replication_role|enum|origin,replica,local|NULL|When this parameter is set, any cached query plan will be lost before.| session_timeout|int|0,86400|s|GaussDB Kernel gsql client has an automatic reconnection mechanism, when the timeout, the gsql will be reconnection after disconnection.| +idle_in_transaction_session_timeout|int|0,86400|s|Sets the maximum allowed idle time between queries, when in a transaction.| shared_buffers|int|16,1073741823|kB|NULL| shared_preload_libraries|string|0,0|NULL|NULL| show_acce_estimate_detail|bool|0,0|NULL|NULL| diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index 4e65ded16..b7936eecd 100755 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -2007,6 +2007,20 @@ static void InitConfigureNamesInt() NULL, NULL, NULL}, + {{"idle_in_transaction_session_timeout", + PGC_USERSET, + NODE_SINGLENODE, + CLIENT_CONN_STATEMENT, + gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."), + gettext_noop("A value of 0 turns off the timeout."), + GUC_UNIT_S}, + &u_sess->attr.attr_common.IdleInTransactionSessionTimeout, + 0, + 0, + MAX_SESSION_TIMEOUT, + NULL, + NULL, + NULL}, {{"track_thread_wait_status_interval", PGC_SUSET, NODE_ALL, diff --git a/src/common/backend/utils/misc/postgresql_single.conf.sample b/src/common/backend/utils/misc/postgresql_single.conf.sample index de84a7648..b5420541f 100644 --- a/src/common/backend/utils/misc/postgresql_single.conf.sample +++ b/src/common/backend/utils/misc/postgresql_single.conf.sample @@ -84,6 +84,7 @@ max_connections = 200 # (change requires restart) #authentication_timeout = 1min # 1s-600s session_timeout = 10min # allowed duration of any unused session, 0s-86400s(1 day), 0 is disabled +#idle_in_transaction_session_timeout = 0 # Sets the maximum allowed idle time between queries, when in a transaction, 0 is disabled #ssl = off # (change requires restart) #ssl_ciphers = 'ALL' # allowed SSL ciphers # (change requires restart) diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index e20ad7589..d6a4172ad 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -723,8 +723,22 @@ static int ReadCommand(StringInfo inBuf) u_sess->postgres_cxt.doing_extended_query_message = false; /* Start a timer for session timeout. */ - if (!enable_session_sig_alarm(u_sess->attr.attr_common.SessionTimeout * 1000)) + if (!enable_session_sig_alarm(u_sess->attr.attr_common.SessionTimeout * 1000)) { ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not set timer for session timeout"))); + } + +#ifndef ENABLE_MULTIPLE_NODES + /* if idle_in_transaction_session_timeout > 0 and it is in a transaction in idle, + * then start a timer for idle_in_transaction_session. + */ + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + (IsAbortedTransactionBlockState() || IsTransactionOrTransactionBlock())) { + if (!enable_idle_in_transaction_session_sig_alarm( + u_sess->attr.attr_common.IdleInTransactionSessionTimeout * 1000)) { + ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not set timer for idle-in-transaction timeout"))); + } + } +#endif if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) result = SocketBackend(inBuf); @@ -734,8 +748,18 @@ static int ReadCommand(StringInfo inBuf) result = EOF; /* Disable a timer for session timeout. */ - if (!disable_session_sig_alarm()) + if(!disable_session_sig_alarm()) { ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not disable timer for session timeout"))); + } + +#ifndef ENABLE_MULTIPLE_NODES + /* Disable a timer for idle_in_transaction_session. */ + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + (IsAbortedTransactionBlockState() || IsTransactionOrTransactionBlock()) && + !disable_idle_in_transaction_session_sig_alarm()) { + ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not disable timer for idle-in-transaction timeout"))); + } +#endif u_sess->proc_cxt.firstChar = (char)result; return result; diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index bcc847a9e..5e1f4be22 100755 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -117,6 +117,10 @@ static void knl_u_attr_init(knl_session_attr* attr) attr->attr_common.client_min_messages = NOTICE; attr->attr_common.SessionTimeout = sessionDefaultTimeout; attr->attr_common.SessionTimeoutCount = 0; +#ifndef ENABLE_MULTIPLE_NODES + attr->attr_common.IdleInTransactionSessionTimeout = 0; + attr->attr_common.IdleInTransactionSessionTimeoutCount = 0; +#endif attr->attr_common.enable_full_encryption = false; attr->attr_storage.sync_method = DEFAULT_SYNC_METHOD; attr->attr_sql.under_explain = false; @@ -1487,6 +1491,12 @@ knl_session_context* create_session_context(MemoryContext parent, uint64 id) * non-threadpool/fake session will direct call ReadCommand and enable alarm by sig timer * */ (void)enable_session_sig_alarm(u_sess->attr.attr_common.SessionTimeout * secondToMilliSecond); +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + (IsAbortedTransactionBlockState() || IsTransactionOrTransactionBlock())) { + (void)enable_idle_in_transaction_session_sig_alarm(u_sess->attr.attr_common.IdleInTransactionSessionTimeout * 1000); + } +#endif } // Switch to context group, in case knl_u_executor_init will alloc memory on CurrentMemoryContext. diff --git a/src/gausskernel/process/threadpool/threadpool_scheduler.cpp b/src/gausskernel/process/threadpool/threadpool_scheduler.cpp index e44aa11ca..b84e72d4a 100644 --- a/src/gausskernel/process/threadpool/threadpool_scheduler.cpp +++ b/src/gausskernel/process/threadpool/threadpool_scheduler.cpp @@ -95,6 +95,9 @@ void TpoolSchedulerMain(ThreadPoolScheduler *scheduler) scheduler->DynamicAdjustThreadPool(); scheduler->GPCScheduleCleaner(&gpc_count); g_threadPoolControler->GetSessionCtrl()->CheckSessionTimeout(); +#ifndef ENABLE_MULTIPLE_NODES + g_threadPoolControler->GetSessionCtrl()->CheckIdleInTransactionSessionTimeout(); +#endif } proc_exit(0); } diff --git a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp index 1267f0ad1..e5b36be82 100755 --- a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp +++ b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp @@ -701,27 +701,58 @@ void ThreadPoolSessControl::CheckSessionTimeout() for (cidx = 0; cidx < m_maxActiveSessionCount; cidx++) { knl_sess_control* ctrl = &m_base[cidx]; knl_session_context* sess = ctrl->sess; - if (sess != NULL && sess->attr.attr_common.SessionTimeout != 0) { - if (sess->storage_cxt.session_timeout_active) { - if (now >= sess->storage_cxt.session_fin_time && sess->attr.attr_common.SessionTimeoutCount < 10) { + if (sess != NULL && sess->attr.attr_common.SessionTimeout != 0 && + sess->storage_cxt.session_timeout_active) { + if (now >= sess->storage_cxt.session_fin_time && sess->attr.attr_common.SessionTimeoutCount < 10) { #ifdef HAVE_INT64_TIMESTAMP - elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %ld. But now is:%ld", - sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1, - sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now); + elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %ld. But now is:%ld", + sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1, + sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now); #else - elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %lf. But now is:%lf", - sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1, - sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now); + elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %lf. But now is:%lf", + sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1, + sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now); #endif - sess->attr.attr_common.SessionTimeoutCount++; - CloseClientSocket(sess, false); - } + sess->attr.attr_common.SessionTimeoutCount++; + CloseClientSocket(sess, false); } } } alock.unLock(); } +#ifndef ENABLE_MULTIPLE_NODES +void ThreadPoolSessControl::CheckIdleInTransactionSessionTimeout() +{ + AutoMutexLock alock(&m_sessCtrlock); + alock.lock(); + int cidx; + TimestampTz now = GetCurrentTimestamp(); + for (cidx = 0; cidx < m_maxActiveSessionCount; cidx++) { + knl_sess_control* ctrl = &m_base[cidx]; + knl_session_context* sess = ctrl->sess; + if (sess != NULL && sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + sess->storage_cxt.idle_in_transaction_session_timeout_active) { + if (now >= sess->storage_cxt.idle_in_transaction_session_fin_time && + sess->attr.attr_common.IdleInTransactionSessionTimeoutCount < 10) { +#ifdef HAVE_INT64_TIMESTAMP + elog(LOG, "close session : %lu for %d times due to idle in transaction timeout : %d, max finish time is %ld. But now is:%ld", + sess->session_id, sess->attr.attr_common.IdleInTransactionSessionTimeoutCount + 1, + sess->attr.attr_common.IdleInTransactionSessionTimeout, sess->storage_cxt.idle_in_transaction_session_fin_time, now); +#else + elog(LOG, "close session : %lu for %d times due to idle in transaction session timeout : %d, max finish time is %lf. But now is:%lf", + sess->session_id, sess->attr.attr_common.IdleInTransactionSessionTimeoutCount + 1, + sess->attr.attr_common.IdleInTransactionSessionTimeout, sess->storage_cxt.idle_in_transaction_session_fin_time, now); +#endif + sess->attr.attr_common.IdleInTransactionSessionTimeout++; + CloseClientSocket(sess, false); + } + } + } + alock.unLock(); +} +#endif + TransactionId ThreadPoolSessControl::ListAllSessionGttFrozenxids(int maxSize, ThreadId *pids, TransactionId *xids, int *n) { diff --git a/src/gausskernel/process/threadpool/threadpool_worker.cpp b/src/gausskernel/process/threadpool/threadpool_worker.cpp index 44cd0042f..5af8881f5 100644 --- a/src/gausskernel/process/threadpool/threadpool_worker.cpp +++ b/src/gausskernel/process/threadpool/threadpool_worker.cpp @@ -171,6 +171,13 @@ void ThreadPoolWorker::WaitMission() } (void)enable_session_sig_alarm(u_sess->attr.attr_common.SessionTimeout * 1000); +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + (IsAbortedTransactionBlockState() || IsTransactionOrTransactionBlock())) { + (void)enable_idle_in_transaction_session_sig_alarm( + u_sess->attr.attr_common.IdleInTransactionSessionTimeout * 1000); + } +#endif bool isRawSession = false; Assert(t_thrd.int_cxt.InterruptHoldoffCount == 0); @@ -226,6 +233,12 @@ void ThreadPoolWorker::WaitMission() } MemoryContextSwitchTo(old); (void)disable_session_sig_alarm(); +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0 && + (IsAbortedTransactionBlockState() || IsTransactionOrTransactionBlock())) { + (void)disable_idle_in_transaction_session_sig_alarm(); + } +#endif /* now we can accept signal. out of this, we rely on signal handle. */ AllowSignal(); ShutDownIfNecessary(); diff --git a/src/gausskernel/storage/lmgr/proc.cpp b/src/gausskernel/storage/lmgr/proc.cpp index 604e269e5..bd7003a57 100755 --- a/src/gausskernel/storage/lmgr/proc.cpp +++ b/src/gausskernel/storage/lmgr/proc.cpp @@ -89,6 +89,9 @@ static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); static bool CheckStatementTimeout(void); static void CheckSessionTimeout(void); +#ifndef ENABLE_MULTIPLE_NODES +static void CheckIdleInTransactionSessionTimeout(void); +#endif static bool CheckStandbyTimeout(void); static void FiniNuma(int code, Datum arg); @@ -2547,6 +2550,54 @@ bool enable_session_sig_alarm(int delayms) return true; } +#ifndef ENABLE_MULTIPLE_NODES +/* Enable the idle_in_transaction_session_timeout timer. */ +bool enable_idle_in_transaction_session_sig_alarm(int delayms) +{ + TimestampTz fin_time; + struct itimerval timeval; + errno_t rc = EOK; + + /* Session timer only work when connect form app, not work for inner conncection. */ + if (!IsConnFromApp() || IS_THREAD_POOL_STREAM) { + return true; + } + /* disable idle_in_transaction session timeout */ + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout == 0) { + return true; + } + int delay_time_ms = delayms; + /* Set idle_in_transaction_session_timeout flag true. */ + u_sess->storage_cxt.idle_in_transaction_session_timeout_active = true; + + /* Calculate idle_in_transaction session timeout finish time. */ + fin_time = GetCurrentTimestamp(); + fin_time = TimestampTzPlusMilliseconds(fin_time, delay_time_ms); + u_sess->storage_cxt.idle_in_transaction_session_fin_time = fin_time; + + /* + * threadpool session status must be UNINIT,ATTACH,DEATCH... + * KNL_SESS_FAKE only use in thread preinit + * if a new session is UNINIT + * if attached, t_thrd is valid. + * if not attach, t_thrd is invalid + * so we user USESS_STATUS to check again + * */ + if (IS_THREAD_POOL_WORKER || u_sess->status != KNL_SESS_FAKE) { + return true; + } + /* Now we set the timer to interrupt. */ + rc = memset_s(&timeval, sizeof(struct itimerval), 0, sizeof(struct itimerval)); + securec_check(rc, "\0", "\0"); + timeval.it_value.tv_sec = delay_time_ms / 1000; + timeval.it_value.tv_usec = (delay_time_ms % 1000) * 1000; + if (gs_signal_settimer(&timeval)) + return false; + + return true; +} +#endif + /* Disable the session timeout timer. */ bool disable_session_sig_alarm(void) { @@ -2576,6 +2627,37 @@ bool disable_session_sig_alarm(void) } } +#ifndef ENABLE_MULTIPLE_NODES +/* Disable the idle_in_transaction_session_timeout timer. */ +bool disable_idle_in_transaction_session_sig_alarm(void) +{ + if (IS_THREAD_POOL_WORKER) { + u_sess->storage_cxt.idle_in_transaction_session_timeout_active = false; + return true; + } + /* idle_in_transaction_session_timeout timer only work when connect from app, not work for inner conncection. */ + if (!IsConnFromApp()) { + return true; + } + + /* already disable */ + if (!u_sess->attr.attr_common.IdleInTransactionSessionTimeout && \ + (!u_sess->storage_cxt.idle_in_transaction_session_timeout_active)) { + return true; + } + /* + * Always disable the timer if it is active; this is for guarantee the + * atomicity of idle_in_transaction_session_timeout timer. + */ + if (u_sess->storage_cxt.idle_in_transaction_session_timeout_active && !gs_signal_canceltimer()) { + u_sess->storage_cxt.idle_in_transaction_session_timeout_active = false; + return true; + } else { + return false; + } +} +#endif + /* * Cancel the SIGALRM timer, either for a deadlock timeout or a statement * timeout. If a deadlock timeout is canceled, any active statement timeout @@ -2722,6 +2804,28 @@ static bool CheckStatementTimeout(void) return true; } +#ifndef ENABLE_MULTIPLE_NODES +/* + * Check for idle_in_transaction timeout. If the timeout time has come, + * trigger a session-cancel interrupt; + */ +static void CheckIdleInTransactionSessionTimeout(void) +{ + if (IS_THREAD_POOL_WORKER) { + return; + } + TimestampTz now = GetCurrentTimestamp(); + if (now >= u_sess->storage_cxt.idle_in_transaction_session_fin_time) { + u_sess->storage_cxt.idle_in_transaction_session_timeout_active = false; + if (u_sess->attr.attr_common.IdleInTransactionSessionTimeout > 0) { + ereport(WARNING, (errmsg("Idle in transaction session unused timeout."))); + (void)gs_signal_canceltimer(); + (void)gs_signal_send(t_thrd.proc_cxt.MyProcPid, SIGTERM); + } + } +} +#endif + /* * Check for session timeout. If the timeout time has come, * trigger a session-cancel interrupt; @@ -2787,6 +2891,12 @@ void handle_sig_alarm(SIGNAL_ARGS) SetLatch(&t_thrd.proc->procLatch); } +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->storage_cxt.idle_in_transaction_session_timeout_active) { + CheckIdleInTransactionSessionTimeout(); + } +#endif + if (u_sess->storage_cxt.session_timeout_active) { CheckSessionTimeout(); } diff --git a/src/include/knl/knl_guc/knl_session_attr_common.h b/src/include/knl/knl_guc/knl_session_attr_common.h index 461581e3b..3310ada2c 100644 --- a/src/include/knl/knl_guc/knl_session_attr_common.h +++ b/src/include/knl/knl_guc/knl_session_attr_common.h @@ -83,6 +83,10 @@ typedef struct knl_session_attr_common { int StatementTimeout; int SessionTimeout; int SessionTimeoutCount; +#ifndef ENABLE_MULTIPLE_NODES + int IdleInTransactionSessionTimeout; + int IdleInTransactionSessionTimeoutCount; +#endif int pgstat_collect_thread_status_interval; int extra_float_digits; int effective_io_concurrency; diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index cde82ff7a..8e6b37bfb 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -1715,6 +1715,10 @@ typedef struct knl_u_storage_context { volatile bool session_timeout_active; /* session_fin_time is valid only if session_timeout_active is true */ TimestampTz session_fin_time; +#ifndef ENABLE_MULTIPLE_NODES + volatile bool idle_in_transaction_session_timeout_active; + TimestampTz idle_in_transaction_session_fin_time; +#endif /* Number of file descriptors known to be in use by VFD entries. */ int nfile; diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 906ea1411..f33a0a4f0 100755 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -477,7 +477,9 @@ extern TimestampTz GetStatementFinTime(); extern bool enable_sig_alarm(int delayms, bool is_statement_timeout); extern bool enable_lockwait_sig_alarm(int delayms); extern bool enable_session_sig_alarm(int delayms); +extern bool enable_idle_in_transaction_session_sig_alarm(int delayms); extern bool disable_session_sig_alarm(void); +extern bool disable_idle_in_transaction_session_sig_alarm(void); extern bool disable_sig_alarm(bool is_statement_timeout); extern bool pause_sig_alarm(bool is_statement_timeout); diff --git a/src/include/threadpool/threadpool_sessctl.h b/src/include/threadpool/threadpool_sessctl.h index 15c1d3eff..369e06e7b 100644 --- a/src/include/threadpool/threadpool_sessctl.h +++ b/src/include/threadpool/threadpool_sessctl.h @@ -60,6 +60,9 @@ public: void SigHupHandler(); void HandlePoolerReload(); void CheckSessionTimeout(); +#ifndef ENABLE_MULTIPLE_NODES + void CheckIdleInTransactionSessionTimeout(); +#endif void CheckPermissionForSendSignal(knl_session_context* sess, sig_atomic_t* lock); void getSessionMemoryDetail(Tuplestorestate* tupStore, TupleDesc tupDesc, knl_sess_control** sess); void getSessionClientInfo(Tuplestorestate* tupStore, TupleDesc tupDesc);