diff --git a/CHANGES.rst b/CHANGES.rst index 78aaf1f907..0a3cd08e0d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,12 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst 5.8.7 (unreleased) ------------------ +- Separate ZODB connection information into new ZODB Connections view. + +- Move the cache detail links to the individual database pages. + +- Fix the auto refresh functionality on the Reference Count page + - Update the Ace editor in the ZMI. - Restrict access to static ZMI resources. diff --git a/src/App/ApplicationManager.py b/src/App/ApplicationManager.py index 07a2e75026..077b15be58 100644 --- a/src/App/ApplicationManager.py +++ b/src/App/ApplicationManager.py @@ -28,6 +28,7 @@ from App.special_dtml import DTMLFile from App.Undo import UndoSupport from App.version_txt import version_txt +from App.ZODBConnectionDebugger import ZODBConnectionDebugger from DateTime.DateTime import DateTime from OFS.Traversable import Traversable from Persistence import Persistent @@ -63,7 +64,9 @@ class DatabaseChooser(Tabs, Traversable, Implicit): {'label': 'Databases', 'action': 'manage_main'}, {'label': 'Configuration', 'action': '../Configuration/manage_main'}, {'label': 'DAV Locks', 'action': '../DavLocks/manage_main'}, - {'label': 'Debug Information', 'action': '../DebugInfo/manage_main'}, + {'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'}, + {'label': 'ZODB Connections', + 'action': '../ZODBConnections/manage_main'}, ) MANAGE_TABS_NO_BANNER = True @@ -108,7 +111,9 @@ class ConfigurationViewer(Tabs, Traversable, Implicit): {'label': 'Databases', 'action': '../Database/manage_main'}, {'label': 'Configuration', 'action': 'manage_main'}, {'label': 'DAV Locks', 'action': '../DavLocks/manage_main'}, - {'label': 'Debug Information', 'action': '../DebugInfo/manage_main'}, + {'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'}, + {'label': 'ZODB Connections', + 'action': '../ZODBConnections/manage_main'}, ) MANAGE_TABS_NO_BANNER = True @@ -150,7 +155,7 @@ class DebugManager(Tabs, Traversable, Implicit): manage = manage_main = manage_workspace = DTMLFile('dtml/debug', globals()) manage_main._setName('manage_main') id = 'DebugInfo' - name = title = 'Debug Information' + name = title = 'Reference Counts' meta_type = name zmi_icon = 'fas fa-bug' @@ -159,7 +164,9 @@ class DebugManager(Tabs, Traversable, Implicit): {'label': 'Databases', 'action': '../Database/manage_main'}, {'label': 'Configuration', 'action': '../Configuration/manage_main'}, {'label': 'DAV Locks', 'action': '../DavLocks/manage_main'}, - {'label': 'Debug Information', 'action': 'manage_main'}, + {'label': 'Reference Counts', 'action': 'manage_main'}, + {'label': 'ZODB Connections', + 'action': '../ZODBConnections/manage_main'}, ) def refcount(self, n=None, t=(type(Implicit), type(object))): @@ -224,10 +231,6 @@ def rcdeltas(self): 'rc': n[1][0], } for n in rd] - def dbconnections(self): - import Zope2 # for data - return Zope2.DB.connectionDebugInfo() - def manage_getSysPath(self): return list(sys.path) @@ -235,8 +238,7 @@ def manage_getSysPath(self): InitializeClass(DebugManager) -class ApplicationManager(CacheManager, - Persistent, +class ApplicationManager(Persistent, Tabs, Traversable, Implicit): @@ -255,6 +257,7 @@ class ApplicationManager(CacheManager, Configuration = ConfigurationViewer() DavLocks = DavLockManager() DebugInfo = DebugManager() + ZODBConnections = ZODBConnectionDebugger() manage = manage_main = DTMLFile('dtml/cpContents', globals()) manage_main._setName('manage_main') @@ -263,7 +266,8 @@ class ApplicationManager(CacheManager, {'label': 'Databases', 'action': 'Database/manage_main'}, {'label': 'Configuration', 'action': 'Configuration/manage_main'}, {'label': 'DAV Locks', 'action': 'DavLocks/manage_main'}, - {'label': 'Debug Information', 'action': 'DebugInfo/manage_main'}, + {'label': 'Reference Counts', 'action': 'DebugInfo/manage_main'}, + {'label': 'ZODB Connections', 'action': 'ZODBConnections/manage_main'}, ) MANAGE_TABS_NO_BANNER = True @@ -311,7 +315,7 @@ def getCLIENT_HOME(self): return getConfiguration().clienthome -class AltDatabaseManager(Traversable, UndoSupport): +class AltDatabaseManager(CacheManager, Traversable, UndoSupport): """ Database management DBTab-style """ id = 'DatabaseManagement' @@ -333,6 +337,9 @@ def _getDB(self): def cache_length(self): return self._getDB().cacheSize() + def cache_active_and_inactive_count(self): + return sum([x['size'] for x in self._getDB().cacheDetailSize()]) + def cache_length_bytes(self): return self._getDB().getCacheSizeBytes() diff --git a/src/App/DavLockManager.py b/src/App/DavLockManager.py index 2e4622da94..28962f7cae 100644 --- a/src/App/DavLockManager.py +++ b/src/App/DavLockManager.py @@ -39,7 +39,9 @@ class DavLockManager(Item, Implicit): {'label': 'Databases', 'action': '../Database/manage_main'}, {'label': 'Configuration', 'action': '../Configuration/manage_main'}, {'label': 'DAV Locks', 'action': 'manage_main'}, - {'label': 'Debug Information', 'action': '../DebugInfo/manage_main'}, + {'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'}, + {'label': 'ZODB Connections', + 'action': '../ZODBConnections/manage_main'}, ) @security.protected(webdav_manage_locks) diff --git a/src/App/ZODBConnectionDebugger.py b/src/App/ZODBConnectionDebugger.py new file mode 100644 index 0000000000..9167d42c32 --- /dev/null +++ b/src/App/ZODBConnectionDebugger.py @@ -0,0 +1,90 @@ +############################################################################## +# +# Copyright (c) 2023 Zope Foundation and Contributors. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +import pprint +import time +from operator import itemgetter + +from AccessControl.class_init import InitializeClass +from AccessControl.SecurityInfo import ClassSecurityInfo +from Acquisition import Implicit +from App.special_dtml import DTMLFile +from OFS.SimpleItem import Item + + +class ZODBConnectionDebugger(Item, Implicit): + id = 'ZODBConnectionDebugger' + name = title = 'ZODB Connections' + meta_type = 'ZODB Connection Debugger' + zmi_icon = 'fas fa-bug' + + security = ClassSecurityInfo() + + manage_zodb_conns = manage_main = manage = manage_workspace = DTMLFile( + 'dtml/zodbConnections', globals()) + manage_zodb_conns._setName('manage_zodb_conns') + manage_options = ( + {'label': 'Control Panel', 'action': '../manage_main'}, + {'label': 'Databases', 'action': '../Database/manage_main'}, + {'label': 'Configuration', 'action': '../Configuration/manage_main'}, + {'label': 'DAV Locks', 'action': '../DavLocks/manage_main'}, + {'label': 'Reference Counts', 'action': '../DebugInfo/manage_main'}, + {'label': 'ZODB Connections', 'action': 'manage_main'}, + ) + + def dbconnections(self): + import Zope2 # for data + + result = [] + now = time.time() + + def get_info(connection): + # `result`, `time` and `before` are lexically inherited. + request_info = {} + request_info_formatted = '' + debug_info_formatted = '' + opened = connection.opened + debug_info = connection.getDebugInfo() or {} + + if debug_info: + debug_info_formatted = pprint.pformat(debug_info) + if len(debug_info) == 2: + request_info = debug_info[0] + request_info.update(debug_info[1]) + request_info_formatted = pprint.pformat(request_info) + + if opened is not None: + # output UTC time with the standard Z time zone indicator + open_since = "{}".format( + time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(opened))) + open_for = "{:.2f}s".format(now - opened) + else: + open_since = '(closed)' + open_for = '' + + result.append({ + 'open_since': open_since, + 'open_for': open_for, + 'info': debug_info, + 'info_formatted': debug_info_formatted, + 'request_info': request_info, + 'request_formatted': request_info_formatted, + 'before': connection.before, + 'cache_size': len(connection._cache), + }) + + Zope2.DB._connectionMap(get_info) + return sorted(result, key=itemgetter('open_since')) + + +InitializeClass(ZODBConnectionDebugger) diff --git a/src/App/dtml/dbMain.dtml b/src/App/dtml/dbMain.dtml index 8b480209e7..2fbf311b9b 100644 --- a/src/App/dtml/dbMain.dtml +++ b/src/App/dtml/dbMain.dtml @@ -91,11 +91,22 @@
+ + + Cache detail + + + + Cache extreme detail + +
+