diff --git a/lib/modules/Sql.pmod/pgsql.h b/lib/modules/Sql.pmod/pgsql.h index 32e2224653401e2de88f89a78b30cc6752484a28..e65ff70fde91de5fe3bab75906bfb6f6f49cd628 100644 --- a/lib/modules/Sql.pmod/pgsql.h +++ b/lib/modules/Sql.pmod/pgsql.h @@ -24,6 +24,7 @@ #define QUERYTIMEOUT 4095 // Queries running longer than this number // of seconds are canceled automatically #define PORTALBUFFERSIZE (32*1024) // Approximate buffer per portal +#define BACKOFFDELAY 1 #define PGSQL_DEFAULT_PORT 5432 #define PGSQL_DEFAULT_HOST "localhost" diff --git a/lib/modules/Sql.pmod/pgsql.pike b/lib/modules/Sql.pmod/pgsql.pike index 704757b78cf9e2107da4568969579aa7424d20d7..5cc10ab53be4e35db99523f7c9d30124e908f96f 100644 --- a/lib/modules/Sql.pmod/pgsql.pike +++ b/lib/modules/Sql.pmod/pgsql.pike @@ -78,6 +78,8 @@ private int prepstmtused; // Number of times we used prepared statements private int cachedepth = STATEMENTCACHEDEPTH; private int portalbuffersize = PORTALBUFFERSIZE; private int timeout = QUERYTIMEOUT; +private array connparmcache; +private int reconnected; protected string _sprintf(int type) { string res; @@ -114,6 +116,12 @@ protected string _sprintf(int type) { //! @param options //! Currently supports at least the following: //! @mapping +//! @member int "reconnect" +//! Set it to zero to disable automatic reconnects upon losing +//! the connection to the database. Not setting it, or setting +//! it to one, will cause one timed reconnect to take place. +//! Setting it to -1 will cause the system to try and reconnect +//! indefinitely. //! @member int "use_ssl" //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will fallback @@ -176,9 +184,10 @@ protected void create(void|string host, void|string database, String.secure(pass); pass = "CENSORED"; } - proxy = .pgsql_util.proxy(host, database, + connparmcache = ({ host, database, user && user != "" ? Standards.IDNA.to_ascii(user, 1) : user, - spass, options || ([])); + spass, options || ([])}); + proxy = .pgsql_util.proxy(@connparmcache); } //! @returns @@ -245,7 +254,8 @@ protected void create(void|string host, void|string database, //! @[is_open()] /*semi*/final int ping() { waitauthready(); - return is_open() && !catch(proxy.c->start()->sendcmd(FLUSHSEND)) ? 0 : -1; + return is_open() + && !catch(proxy.c->start()->sendcmd(FLUSHSEND)) ? !!reconnected : -1; } //! Cancels all currently running queries in this session. @@ -881,6 +891,15 @@ private inline void throwdelayederror(object parent) { private void startquery(int forcetext, .pgsql_util.sql_result portal, string q, mapping(string:mixed) tp, string preparedname) { .pgsql_util.conxion c = proxy.c; + if (!c && (proxy.options["reconnect"] + || zero_type(proxy.options["reconnect"]))) { + sleep(BACKOFFDELAY); // Force a backoff delay + if (!proxy.c) { + reconnected++; + proxy = .pgsql_util.proxy(@connparmcache); + } + c = proxy.c; + } if (forcetext) { // FIXME What happens if portals are still open? portal._unnamedportalkey = proxy.unnamedportalmux->lock(1); portal._portalname = ""; diff --git a/lib/modules/Sql.pmod/pgsql_util.pmod b/lib/modules/Sql.pmod/pgsql_util.pmod index f5cd54fac35092b8482f60bc374377d986d90f0a..be350e33b68ff4d48fea7625130a025f4f5c54be 100644 --- a/lib/modules/Sql.pmod/pgsql_util.pmod +++ b/lib/modules/Sql.pmod/pgsql_util.pmod @@ -1496,7 +1496,7 @@ class proxy { final int(0..1) invalidatecache; private Thread.Queue qportals; final mixed delayederror; - private function (:void) readyforquery_cb; + final function (:void) readyforquery_cb; final string host; final int(0..65535) port; @@ -1698,11 +1698,12 @@ class proxy { }; #endif int msgisfatal(mapping(string:string) msgresponse) { - if (!terminating) // Run the callback once per lost connection - runcallback(backendpid,"_lost",""); - return (has_prefix(msgresponse.C, "53") - || has_prefix(msgresponse.C, "3D") - || has_prefix(msgresponse.C, "57P")) && MAGICTERMINATE; + int isfatal = (has_prefix(msgresponse.C, "53") + || has_prefix(msgresponse.C, "3D") + || has_prefix(msgresponse.C, "57P")) && MAGICTERMINATE; + if (isfatal && !terminating) // Run the callback once per lost connection + runcallback(backendpid, "_lost", ""); + return isfatal; }; for (;;) { err = catch { @@ -2394,13 +2395,15 @@ class proxy { } } - private void sendsync() { + final void sendsync() { readyforquerycount++; c->start()->sendcmd(SYNCSEND); } private void runcallback(int pid, string condition, string extrainfo) { array cb; + if (condition == "_lost") + destruct(c); if ((cb = notifylist[condition] || notifylist[""]) && (pid != backendpid || sizeof(cb) > 1 && cb[1])) callout(cb[0], 0, pid, condition, extrainfo, @cb[2..]);