public inbox for [email protected]
help / color / mirror / Atom feedFrom: Aditya Toshniwal <[email protected]>
To: pgadmin-hackers <[email protected]>
Cc: Akshay Joshi <[email protected]>
Subject: Re: [pgAdmin][RM4818] Unable to handle loss of connection to server
Date: Thu, 21 Nov 2019 11:50:58 +0530
Message-ID: <CAM9w-_kuy3kKxFUd8+NqteyPH5rPti7TSr36qAx623zuveTgvg@mail.gmail.com> (raw)
In-Reply-To: <CANxoLDdnkx2FeHiiL4O9khjiPk_8MCWLKDbMN8-eUWiy6NQhOg@mail.gmail.com>
References: <CAM9w-_mEu_23wQFWAyjqjbqjb_LF1bZb-94B++xGAXEWyjZ0rg@mail.gmail.com>
<CANxoLDdnkx2FeHiiL4O9khjiPk_8MCWLKDbMN8-eUWiy6NQhOg@mail.gmail.com>
Hi Hackers,
Attached is the updated patch to fix the review comments.
Please note, there is some problem with psycopg2/libpq where it hangs when
network is lost - https://github.com/psycopg/psycopg2/issues/561
I've also changed the code to make connections after the "Servers" node is
expanded to fix - RM4933, so that the servers are listed first.
Kindly review.
On Fri, Nov 15, 2019 at 2:34 PM Akshay Joshi <[email protected]>
wrote:
> Hi Aditya
>
> Following are the review comments:
>
> 1. The default connection timeout should be *10* seconds.
> 2. In case the network is disabled and when we expand the "Servers"
> node the database server(for which network is disabled) should be shown in
> the disconnected state.
> 3. For the above case when you try to connect to the server and at
> the same time trying to fetch the properties of the valid database server,
> it takes long time to fetch it.
> 4. Case when the network is disabled, the database servers password
> is saved and pgAdmin4 tries to connect the server, after connection
> timeout, there is no message to the user, only spinner gets disappeared. We
> should show a message.
> 5. We should improve the error messages "timeout expired" and
> "Connection to the servers has been lost." to the more meaningful messages
> like "Check the network settings, check the firewall status."
> 6. Install database server on Windows and Enable Windows Firewall. Try
> to connect to the same database server. Spinner is not showing, as a user I
> am not able to understand is there any action started at the backend or
> not. As a user, I'll keep trying to connect multiple times and after the
> connection timeout of each request dialog pops up.
>
>
> On Thu, Nov 14, 2019 at 12:15 PM Aditya Toshniwal <
> [email protected]> wrote:
>
>> Hi Hackers,
>>
>> Attached is the workaround patch to set the default connection timeout to
>> 30 seconds instead of the current value of infinite - 0, zero. The input is
>> present in the server properties dialogs advanced tab. The workaround
>> applies to the RM4724 as well.
>> The permanent solution would be to expand the nodes asynchronously -
>> https://redmine.postgresql.org/issues/4933
>>
>> Also fixed a bug where sql editor is not closing if the server is
>> disconnected in between.
>>
>> Kindly review.
>>
>> --
>> Thanks and Regards,
>> Aditya Toshniwal
>> Sr. Software Engineer | EnterpriseDB India | Pune
>> "Don't Complain about Heat, Plant a TREE"
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
>
> *Sr. Software Architect*
> *EnterpriseDB Software India Private Limited*
> *Mobile: +91 976-788-8246*
>
--
Thanks and Regards,
Aditya Toshniwal
Sr. Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
Attachments:
[application/octet-stream] RM4818_v2.patch (30.4K, 3-RM4818_v2.patch)
download | inline diff:
diff --git a/web/migrations/versions/aff1436e3c8c_.py b/web/migrations/versions/aff1436e3c8c_.py
new file mode 100644
index 000000000..45cfb9b3d
--- /dev/null
+++ b/web/migrations/versions/aff1436e3c8c_.py
@@ -0,0 +1,27 @@
+
+""" Update the default timeout to 30 seconds instead on 0.
+0 indicates wait indefinitely which causes trouble when network connection
+to server is lost.
+
+Revision ID: aff1436e3c8c
+Revises: a77a0932a568
+Create Date: 2019-10-28 12:47:36.828709
+
+"""
+from pgadmin.model import db
+
+# revision identifiers, used by Alembic.
+revision = 'aff1436e3c8c'
+down_revision = 'a77a0932a568'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ db.engine.execute(
+ 'UPDATE server SET connect_timeout=10 WHERE connect_timeout=0 OR connect_timeout IS NULL'
+ )
+
+
+def downgrade():
+ pass
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index dc39a1393..6b6ae4c53 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -28,6 +28,7 @@ from pgadmin.model import db, Server, ServerGroup, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.master_password import get_crypt_key
from pgadmin.utils.exception import CryptKeyMissing
+from psycopg2 import Error as psycopg2_Error
def has_any(data, keys):
@@ -58,7 +59,7 @@ def recovery_state(connection, postgres_version):
else:
in_recovery = None
wal_paused = None
- return in_recovery, wal_paused
+ return status, result, in_recovery, wal_paused
def server_icon_and_background(is_connected, manager, server):
@@ -121,19 +122,21 @@ class ServerModule(sg.ServerGroupPluginModule):
for server in servers:
connected = False
manager = None
+ errmsg = None
+ was_connected = False
+ in_recovery = None
+ wal_paused = None
try:
manager = driver.connection_manager(server.id)
conn = manager.connection()
- connected = conn.connected()
+ was_connected = conn.wasConnected
except CryptKeyMissing:
# show the nodes at least even if not able to connect.
pass
+ except psycopg2_Error as e:
+ current_app.logger.exception(e)
+ errmsg = str(e)
- in_recovery = None
- wal_paused = None
-
- if connected:
- in_recovery, wal_paused = recovery_state(conn, manager.version)
yield self.generate_browser_node(
"%d" % (server.id),
gid,
@@ -151,7 +154,9 @@ class ServerModule(sg.ServerGroupPluginModule):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
- if server.tunnel_password is not None else False
+ if server.tunnel_password is not None else False,
+ was_connected=was_connected,
+ errmsg=errmsg
)
@property
@@ -352,12 +357,16 @@ class ServerNode(PGChildNodeView):
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
-
+ errmsg = None
+ in_recovery = None
+ wal_paused = None
if connected:
- in_recovery, wal_paused = recovery_state(conn, manager.version)
- else:
- in_recovery = None
- wal_paused = None
+ status, result, in_recovery, wal_paused =\
+ recovery_state(conn, manager.version)
+ if not status:
+ connected = False
+ manager.release()
+ errmsg = "{0} : {1}".format(server.name, result)
res.append(
self.blueprint.generate_browser_node(
@@ -377,7 +386,8 @@ class ServerNode(PGChildNodeView):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
- if server.tunnel_password is not None else False
+ if server.tunnel_password is not None else False,
+ errmsg=errmsg
)
)
@@ -409,12 +419,16 @@ class ServerNode(PGChildNodeView):
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
-
+ errmsg = None
+ in_recovery = None
+ wal_paused = None
if connected:
- in_recovery, wal_paused = recovery_state(conn, manager.version)
- else:
- in_recovery = None
- wal_paused = None
+ status, result, in_recovery, wal_paused =\
+ recovery_state(conn, manager.version)
+ if not status:
+ connected = False
+ manager.release()
+ errmsg = "{0} : {1}".format(server.name, result)
return make_json_response(
result=self.blueprint.generate_browser_node(
@@ -434,8 +448,9 @@ class ServerNode(PGChildNodeView):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
- if server.tunnel_password is not None else False
- )
+ if server.tunnel_password is not None else False,
+ errmsg=errmsg
+ ),
)
@login_required
@@ -949,19 +964,33 @@ class ServerNode(PGChildNodeView):
def connect_status(self, gid, sid):
"""Check and return the connection status."""
+ server = Server.query.filter_by(id=sid).first()
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
- res = conn.connected()
+ connected = conn.connected()
+ in_recovery = None
+ wal_paused = None
+ errmsg = None
+ if connected:
+ status, result, in_recovery, wal_paused =\
+ recovery_state(conn, manager.version)
- if res:
- from pgadmin.utils.exception import ConnectionLost, \
- SSHTunnelConnectionLost
- try:
- conn.execute_scalar('SELECT 1')
- except (ConnectionLost, SSHTunnelConnectionLost):
- res = False
+ if not status:
+ connected = False
+ manager.release()
+ errmsg = "{0} : {1}".format(server.name, result)
- return make_json_response(data={'connected': res})
+ return make_json_response(
+ data={
+ 'icon': server_icon_and_background(connected, manager, server),
+ 'connected': connected,
+ 'in_recovery': in_recovery,
+ 'wal_pause': wal_paused,
+ 'server_type': manager.server_type if connected else "pg",
+ 'user': manager.user_info if connected else None,
+ 'errmsg': errmsg
+ }
+ )
def connect(self, gid, sid):
"""
@@ -1125,7 +1154,8 @@ class ServerNode(PGChildNodeView):
%s - %s' % (server.id, server.name))
# Update the recovery and wal pause option for the server
# if connected successfully
- in_recovery, wal_paused = recovery_state(conn, manager.version)
+ _, _, in_recovery, wal_paused =\
+ recovery_state(conn, manager.version)
return make_json_response(
success=1,
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index 99fed949e..08364783e 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -292,6 +292,10 @@ define('pgadmin.node.server', [
// Call added method of node.js
pgAdmin.Browser.Node.callbacks.added.apply(this, arguments);
+
+ if(data.was_connected) {
+ fetch_connection_status(this, data, pgBrowser.tree, item);
+ }
return true;
},
/* Reload configuration */
@@ -732,7 +736,7 @@ define('pgadmin.node.server', [
tunnel_password: undefined,
tunnel_authentication: 0,
save_tunnel_password: false,
- connect_timeout: 0,
+ connect_timeout: 10,
},
// Default values!
initialize: function(attrs, args) {
@@ -1273,7 +1277,14 @@ define('pgadmin.node.server', [
}
};
+ /* Wait till the existing request completes */
+ if(data.is_connecting) {
+ return;
+ }
data.is_connecting = true;
+ tree.setLeaf(item);
+ tree.removeIcon(item);
+ tree.addIcon(item, {icon: 'icon-server-connecting'});
var url = obj.generate_url(item, 'connect', data, true);
$.post(url)
.done(function(res) {
@@ -1289,6 +1300,37 @@ define('pgadmin.node.server', [
);
});
};
+ var fetch_connection_status = function(obj, data, tree, item) {
+ var url = obj.generate_url(item, 'connect', data, true);
+
+ tree.setLeaf(item);
+ tree.removeIcon(item);
+ tree.addIcon(item, {icon: 'icon-server-connecting'});
+ $.get(url)
+ .done(function(res) {
+ tree.setInode(item);
+ if (res && res.data) {
+ if (typeof res.data.icon == 'string') {
+ tree.removeIcon(item);
+ data.icon = res.data.icon;
+ tree.addIcon(item, {icon: data.icon});
+ }
+ _.extend(data, res.data);
+
+ var serverInfo = pgBrowser.serverInfo = pgBrowser.serverInfo || {};
+ serverInfo[data._id] = _.extend({}, data);
+
+ if(data.errmsg) {
+ Alertify.error(data.errmsg);
+ }
+ }
+ })
+ .fail(function(xhr, status, error) {
+ tree.setInode(item);
+ tree.addIcon(item, {icon: 'icon-server-not-connected'});
+ Alertify.pgRespErrorNotify(xhr, error);
+ });
+ };
}
return pgBrowser.Nodes['server'];
diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py
index 2a17b3a2d..67dce0942 100644
--- a/web/pgadmin/browser/utils.py
+++ b/web/pgadmin/browser/utils.py
@@ -17,7 +17,8 @@ from flask.views import View, MethodViewType, with_metaclass
from flask_babelex import gettext
from config import PG_DEFAULT_DRIVER
-from pgadmin.utils.ajax import make_json_response, precondition_required
+from pgadmin.utils.ajax import make_json_response, precondition_required,\
+ internal_server_error
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
CryptKeyMissing
@@ -377,11 +378,7 @@ class PGChildNodeView(NodeView):
if not conn.connected():
status, msg = conn.connect()
if not status:
- return precondition_required(
- gettext(
- "Connection to the server has been lost."
- )
- )
+ return internal_server_error(errormsg=msg)
except (ConnectionLost, SSHTunnelConnectionLost, CryptKeyMissing):
raise
except Exception as e:
diff --git a/web/pgadmin/dashboard/static/js/dashboard.js b/web/pgadmin/dashboard/static/js/dashboard.js
index 42d2eca4b..6e466052a 100644
--- a/web/pgadmin/dashboard/static/js/dashboard.js
+++ b/web/pgadmin/dashboard/static/js/dashboard.js
@@ -543,6 +543,7 @@ define('pgadmin.dashboard', [
);
const WAIT_COUNTER = 3;
let last_poll_wait_counter = 0;
+ let resp_not_received_counter = 0;
/* Stop if running, only one poller lives */
self.stopChartsPoller();
@@ -563,7 +564,7 @@ define('pgadmin.dashboard', [
/* If none of the chart wants data, don't trouble
* If response not received from prev poll, don't trouble !!
*/
- if(chart_names_to_get.length == 0 || last_poll_wait_counter > 0) {
+ if(chart_names_to_get.length == 0 || last_poll_wait_counter > 0 || resp_not_received_counter >= WAIT_COUNTER) {
/* reduce the number of tries, request should be sent if last_poll_wait_counter
* completes WAIT_COUNTER times.*/
last_poll_wait_counter--;
@@ -571,12 +572,12 @@ define('pgadmin.dashboard', [
}
var path = self.getStatsUrl(sid, did, chart_names_to_get);
+ resp_not_received_counter++;
$.ajax({
url: path,
type: 'GET',
})
.done(function(resp) {
- last_poll_wait_counter = 0;
for(let chart_name in resp) {
let chart_obj = chart_store[chart_name].chart_obj;
$(chart_obj.getContainer()).removeClass('graph-error');
@@ -584,7 +585,6 @@ define('pgadmin.dashboard', [
}
})
.fail(function(xhr) {
- last_poll_wait_counter = 0;
let err = '';
let msg = '';
let cls = 'info';
@@ -613,6 +613,10 @@ define('pgadmin.dashboard', [
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
);
}
+ })
+ .always(function() {
+ last_poll_wait_counter = 0;
+ resp_not_received_counter--;
});
last_poll_wait_counter = WAIT_COUNTER;
};
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 629e98f37..b22868fbb 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
#
##########################################################################
-SCHEMA_VERSION = 23
+SCHEMA_VERSION = 24
##########################################################################
#
diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js
index f714c2b50..63ad7721b 100644
--- a/web/pgadmin/static/js/sqleditor/execute_query.js
+++ b/web/pgadmin/static/js/sqleditor/execute_query.js
@@ -12,6 +12,7 @@ import $ from 'jquery';
import url_for from '../url_for';
import axios from 'axios';
import * as httpErrorHandler from './query_tool_http_error_handler';
+import * as queryTxnStatus from 'sources/sqleditor/query_txn_status_constants';
class LoadingScreen {
constructor(sqlEditor) {
@@ -83,7 +84,8 @@ class ExecuteQuery {
self.loadingScreen.hide();
self.enableSQLEditorButtons();
// Enable/Disable commit and rollback button.
- if (result.data.data.transaction_status == 2 || result.data.data.transaction_status == 3) {
+ if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
+ || result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
self.enableTransactionButtons();
} else {
self.disableTransactionButtons();
@@ -123,7 +125,8 @@ class ExecuteQuery {
self.updateSqlEditorLastTransactionStatus(httpMessage.data.data.transaction_status);
// Enable/Disable commit and rollback button.
- if (httpMessage.data.data.transaction_status == 2 || httpMessage.data.data.transaction_status == 3) {
+ if (httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
+ || httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
self.enableTransactionButtons();
} else {
self.disableTransactionButtons();
@@ -131,7 +134,7 @@ class ExecuteQuery {
if (ExecuteQuery.isQueryFinished(httpMessage)) {
if (this.sqlServerObject.close_on_idle_transaction &&
- httpMessage.data.data.transaction_status == 0)
+ httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_IDLE)
this.sqlServerObject.check_needed_confirmations_before_closing_panel();
self.loadingScreen.setMessage('Loading data from the database server and rendering...');
diff --git a/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js b/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
new file mode 100644
index 000000000..075264296
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
@@ -0,0 +1,11 @@
+/* psycopg2 transaction status constants
+ * http://initd.org/psycopg/docs/extensions.html#transaction-status-constants
+ */
+
+module.exports = {
+ TRANSACTION_STATUS_IDLE: 0,
+ TRANSACTION_STATUS_ACTIVE: 1,
+ TRANSACTION_STATUS_INTRANS: 2,
+ TRANSACTION_STATUS_INERROR: 3,
+ TRANSACTION_STATUS_UNKNOWN: 5,
+};
diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js
index 33397e80d..e317aeb81 100644
--- a/web/pgadmin/static/js/tree/tree.js
+++ b/web/pgadmin/static/js/tree/tree.js
@@ -9,6 +9,7 @@
import {isValidData} from 'sources/utils';
import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
export class TreeNode {
constructor(id, data, domNode, parent) {
@@ -261,6 +262,9 @@ export class Tree {
const parentId = this.translateTreeNodeIdFromACITree(api.parent(item));
this.addNewNode(id, data, item, parentId);
+ if(data.errmsg) {
+ Alertify.error(data.errmsg);
+ }
}
}
}.bind(this));
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 9d9ab5c84..8f5897f3f 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -36,6 +36,7 @@ define('tools.querytool', [
'sources/sqleditor/calculate_query_run_time',
'sources/sqleditor/call_render_after_poll',
'sources/sqleditor/query_tool_preferences',
+ 'sources/sqleditor/query_txn_status_constants',
'sources/csrf',
'tools/datagrid/static/js/datagrid_panel_title',
'sources/window',
@@ -52,7 +53,7 @@ define('tools.querytool', [
XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
- modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, csrfToken, panelTitleFunc,
+ modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
pgWindow) {
/* Return back, this has been called more than once */
if (pgAdmin.SqlEditor)
@@ -4182,8 +4183,9 @@ define('tools.querytool', [
self.unsaved_changes_user_confirmation(msg, false);
} // If a transaction is currently ongoing
else if (self.preferences.prompt_commit_transaction
- && self.last_transaction_status > 0) { // 0 -> idle (no transaction)
- var is_commit_disabled = self.last_transaction_status == 3; // 3 -> Failed transaction
+ && (self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INTRANS
+ || self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INERROR)) {
+ var is_commit_disabled = self.last_transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR;
self.uncommitted_transaction_user_confirmation(is_commit_disabled);
}
else {
diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py
index 4b3e2204b..94dc2976c 100644
--- a/web/pgadmin/utils/driver/psycopg2/__init__.py
+++ b/web/pgadmin/utils/driver/psycopg2/__init__.py
@@ -14,19 +14,21 @@ object.
"""
import datetime
-from flask import session, request
+from flask import session
from flask_login import current_user
-from flask_babelex import gettext
import psycopg2
from psycopg2.extensions import adapt
+from threading import Lock
import config
-from pgadmin.model import Server, User
+from pgadmin.model import Server
from .keywords import ScanKeyword
from ..abstract import BaseDriver
from .connection import Connection
from .server_manager import ServerManager
+connection_restore_lock = Lock()
+
class Driver(BaseDriver):
"""
@@ -80,21 +82,30 @@ class Driver(BaseDriver):
return None
if session.sid not in self.managers:
- self.managers[session.sid] = managers = dict()
- if '__pgsql_server_managers' in session:
- session_managers = session['__pgsql_server_managers'].copy()
-
- for server in Server.query.filter_by(user_id=current_user.id):
- manager = managers[str(server.id)] = ServerManager(server)
- if server.id in session_managers:
- manager._restore(session_managers[server.id])
- manager.update_session()
+ with connection_restore_lock:
+ # The wait is over but the object might have been loaded
+ # by some other thread check again
+ if session.sid not in self.managers:
+ self.managers[session.sid] = managers = dict()
+ if '__pgsql_server_managers' in session:
+ session_managers =\
+ session['__pgsql_server_managers'].copy()
+ for server in \
+ Server.query.filter_by(
+ user_id=current_user.id):
+ manager = managers[str(server.id)] =\
+ ServerManager(server)
+ if server.id in session_managers:
+ manager._restore(session_managers[server.id])
+ manager.update_session()
+
else:
managers = self.managers[session.sid]
if str(sid) in managers:
manager = managers[str(sid)]
- manager._restore_connections()
- manager.update_session()
+ with connection_restore_lock:
+ manager._restore_connections()
+ manager.update_session()
managers['pinged'] = datetime.datetime.now()
if str(sid) not in managers:
diff --git a/web/pgadmin/utils/driver/psycopg2/server_manager.py b/web/pgadmin/utils/driver/psycopg2/server_manager.py
index 8ae46c2ee..1676f7e38 100644
--- a/web/pgadmin/utils/driver/psycopg2/server_manager.py
+++ b/web/pgadmin/utils/driver/psycopg2/server_manager.py
@@ -25,13 +25,10 @@ from pgadmin.model import Server, User
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
CryptKeyMissing
from pgadmin.utils.master_password import get_crypt_key
-from threading import Lock
if config.SUPPORT_SSH_TUNNEL:
from sshtunnel import SSHTunnelForwarder, BaseSSHTunnelForwarderError
-connection_restore_lock = Lock()
-
class ServerManager(object):
"""
@@ -292,80 +289,79 @@ WHERE db.oid = {0}""".format(did))
connections = data['connections']
- with connection_restore_lock:
- for conn_id in connections:
- conn_info = connections[conn_id]
- if conn_info['conn_id'] in self.connections:
- conn = self.connections[conn_info['conn_id']]
- else:
- conn = self.connections[conn_info['conn_id']] = Connection(
- self, conn_info['conn_id'], conn_info['database'],
- conn_info['auto_reconnect'], conn_info['async_'],
- use_binary_placeholder=conn_info[
- 'use_binary_placeholder'],
- array_to_string=conn_info['array_to_string']
- )
+ for conn_id in connections:
+ conn_info = connections[conn_id]
+ if conn_info['conn_id'] in self.connections:
+ conn = self.connections[conn_info['conn_id']]
+ else:
+ conn = self.connections[conn_info['conn_id']] = Connection(
+ self, conn_info['conn_id'], conn_info['database'],
+ conn_info['auto_reconnect'], conn_info['async_'],
+ use_binary_placeholder=conn_info[
+ 'use_binary_placeholder'],
+ array_to_string=conn_info['array_to_string']
+ )
- # only try to reconnect if connection was connected previously
- # and auto_reconnect is true.
- if conn_info['wasConnected'] and conn_info['auto_reconnect']:
- try:
- # Check SSH Tunnel needs to be created
- if self.use_ssh_tunnel == 1 and \
- not self.tunnel_created:
- status, error = self.create_ssh_tunnel(
- data['tunnel_password'])
-
- # Check SSH Tunnel is alive or not.
- self.check_ssh_tunnel_alive()
-
- conn.connect(
- password=data['password'],
- server_types=ServerType.types()
- )
- # This will also update wasConnected flag in
- # connection so no need to update the flag manually.
- except CryptKeyMissing:
- # maintain the status as this will help to restore once
- # the key is available
- conn.wasConnected = conn_info['wasConnected']
- conn.auto_reconnect = conn_info['auto_reconnect']
- except Exception as e:
- current_app.logger.exception(e)
- self.connections.pop(conn_info['conn_id'])
- raise
+ # only try to reconnect if connection was connected previously
+ # and auto_reconnect is true.
+ if conn_info['wasConnected'] and conn_info['auto_reconnect']:
+ try:
+ # Check SSH Tunnel needs to be created
+ if self.use_ssh_tunnel == 1 and \
+ not self.tunnel_created:
+ status, error = self.create_ssh_tunnel(
+ data['tunnel_password'])
+
+ # Check SSH Tunnel is alive or not.
+ self.check_ssh_tunnel_alive()
+
+ conn.connect(
+ password=data['password'],
+ server_types=ServerType.types()
+ )
+ # This will also update wasConnected flag in
+ # connection so no need to update the flag manually.
+ except CryptKeyMissing:
+ # maintain the status as this will help to restore once
+ # the key is available
+ conn.wasConnected = conn_info['wasConnected']
+ conn.auto_reconnect = conn_info['auto_reconnect']
+ except Exception as e:
+ current_app.logger.exception(e)
+ self.connections.pop(conn_info['conn_id'])
+ raise
def _restore_connections(self):
- with connection_restore_lock:
- for conn_id in self.connections:
- conn = self.connections[conn_id]
- # only try to reconnect if connection was connected previously
- # and auto_reconnect is true.
- wasConnected = conn.wasConnected
- auto_reconnect = conn.auto_reconnect
- if conn.wasConnected and conn.auto_reconnect:
- try:
- # Check SSH Tunnel needs to be created
- if self.use_ssh_tunnel == 1 and \
- not self.tunnel_created:
- status, error = self.create_ssh_tunnel(
- self.tunnel_password
- )
-
- # Check SSH Tunnel is alive or not.
- self.check_ssh_tunnel_alive()
+ for conn_id in self.connections:
+ conn = self.connections[conn_id]
+ # only try to reconnect if connection was connected previously
+ # and auto_reconnect is true.
+ wasConnected = conn.wasConnected
+ auto_reconnect = conn.auto_reconnect
+ if conn.wasConnected and conn.auto_reconnect:
+ try:
+ # Check SSH Tunnel needs to be created
+ if self.use_ssh_tunnel == 1 and \
+ not self.tunnel_created:
+ status, error = self.create_ssh_tunnel(
+ self.tunnel_password
+ )
- conn.connect()
- # This will also update wasConnected flag in
- # connection so no need to update the flag manually.
- except CryptKeyMissing:
- # maintain the status as this will help to restore once
- # the key is available
- conn.wasConnected = wasConnected
- conn.auto_reconnect = auto_reconnect
- except Exception as e:
- current_app.logger.exception(e)
- raise
+ # Check SSH Tunnel is alive or not.
+ self.check_ssh_tunnel_alive()
+
+ conn.connect()
+ # This will also update wasConnected flag in
+ # connection so no need to update the flag manually.
+ except CryptKeyMissing:
+ # maintain the status as this will help to restore once
+ # the key is available
+ conn.wasConnected = wasConnected
+ conn.auto_reconnect = auto_reconnect
+ except Exception as e:
+ self.connections.pop(conn_id)
+ current_app.logger.exception(e)
+ raise
def release(self, database=None, conn_id=None, did=None):
# Stop the SSH tunnel if release() function calls without
view thread (7+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: [pgAdmin][RM4818] Unable to handle loss of connection to server
In-Reply-To: <CAM9w-_kuy3kKxFUd8+NqteyPH5rPti7TSr36qAx623zuveTgvg@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox