jaaql-middleware-python 5.3.2__tar.gz → 5.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. {jaaql_middleware_python-5.3.2/jaaql_middleware_python.egg-info → jaaql_middleware_python-5.3.4}/PKG-INFO +1 -1
  2. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/constants.py +1 -1
  3. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/db/db_interface.py +20 -3
  4. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/db/db_pg_interface.py +37 -1
  5. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/model.py +5 -0
  6. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4/jaaql_middleware_python.egg-info}/PKG-INFO +1 -1
  7. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/LICENSE.txt +0 -0
  8. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/README.md +0 -0
  9. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/__init__.py +0 -0
  10. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/config/__init__.py +0 -0
  11. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/config/config-docker.ini +0 -0
  12. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/config/config-test.ini +0 -0
  13. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/config/config.ini +0 -0
  14. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/config_constants.py +0 -0
  15. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/db/__init__.py +0 -0
  16. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/db/db_utils.py +0 -0
  17. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/db/db_utils_no_circ.py +0 -0
  18. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/documentation/__init__.py +0 -0
  19. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/documentation/documentation_internal.py +0 -0
  20. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/documentation/documentation_public.py +0 -0
  21. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/documentation/documentation_shared.py +0 -0
  22. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/email/__init__.py +0 -0
  23. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/email/email_manager.py +0 -0
  24. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/email/email_manager_service.py +0 -0
  25. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/email/patch_ems.py +0 -0
  26. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/exceptions/__init__.py +0 -0
  27. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/exceptions/custom_http_status.py +0 -0
  28. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/exceptions/http_status_exception.py +0 -0
  29. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/exceptions/jaaql_interpretable_handled_errors.py +0 -0
  30. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/exceptions/not_yet_implement_exception.py +0 -0
  31. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/generated_constants.py +0 -0
  32. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/interpreter/__init__.py +0 -0
  33. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/interpreter/interpret_jaaql.py +0 -0
  34. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/jaaql.py +0 -0
  35. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/migrations/__init__.py +0 -0
  36. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/migrations/migrations.py +0 -0
  37. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/__init__.py +0 -0
  38. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/base_controller.py +0 -0
  39. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/base_model.py +0 -0
  40. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/controller.py +0 -0
  41. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/controller_interface.py +0 -0
  42. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/exception_queries.py +0 -0
  43. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/generated_queries.py +0 -0
  44. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/handmade_queries.py +0 -0
  45. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/model_interface.py +0 -0
  46. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/mvc/response.py +0 -0
  47. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/openapi/__init__.py +0 -0
  48. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/openapi/swagger_documentation.py +0 -0
  49. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/patch.py +0 -0
  50. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/01.install_domains.generated.sql +0 -0
  51. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/02.install_super_user.exceptions.sql +0 -0
  52. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/03.install_super_user.handwritten.sql +0 -0
  53. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/04.install_jaaql_data_structures.generated.sql +0 -0
  54. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/05.install_static_data.generated.sql +0 -0
  55. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/06.install_jaaql.exceptions.sql +0 -0
  56. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/ZZZZ.generated_functions_views_and_permissions.sql +0 -0
  57. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/ZZZZ.reset_references.sql +0 -0
  58. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/scripts/swagger_template.html +0 -0
  59. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/__init__.py +0 -0
  60. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/cached_canned_query_service.py +0 -0
  61. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/migrations_manager_service.py +0 -0
  62. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/patch_mms.py +0 -0
  63. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/patch_shared_var_service.py +0 -0
  64. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/services/shared_var_service.py +0 -0
  65. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/__init__.py +0 -0
  66. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/bootstrap_secrets.py +0 -0
  67. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/cron.py +0 -0
  68. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/crypt_utils.py +0 -0
  69. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/options.py +0 -0
  70. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/utils.py +0 -0
  71. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/utils_no_project_imports.py +0 -0
  72. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql/utilities/vault.py +0 -0
  73. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql_middleware_python.egg-info/SOURCES.txt +0 -0
  74. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql_middleware_python.egg-info/dependency_links.txt +0 -0
  75. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql_middleware_python.egg-info/requires.txt +0 -0
  76. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/jaaql_middleware_python.egg-info/top_level.txt +0 -0
  77. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/setup.cfg +0 -0
  78. {jaaql_middleware_python-5.3.2 → jaaql_middleware_python-5.3.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaaql-middleware-python
3
- Version: 5.3.2
3
+ Version: 5.3.4
4
4
  Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
5
5
  Home-page: https://github.com/JAAQL/JAAQL-middleware-python
6
6
  Author: Software Quality Measurement and Improvement bv
@@ -218,5 +218,5 @@ ROLE__dba = "dba"
218
218
 
219
219
  PROTOCOL__postgres = "postgresql://"
220
220
 
221
- VERSION = "5.3.2"
221
+ VERSION = "5.3.4"
222
222
 
@@ -77,6 +77,11 @@ class DBInterface(ABC):
77
77
  logging.warning(exc, exc_info=False)
78
78
 
79
79
  def __attempt_commit_rollback(self, conn, err):
80
+ # Returns the commit failure rather than swallowing it. A failed commit means the transaction
81
+ # did NOT persist, so the caller must not report success. Silently swallowing it turned lost
82
+ # writes into a misleading downstream error - e.g. create_account's CREATE ROLE lost on a
83
+ # connection killed by a \wipe dbms reboot, then "GRANT registered TO <role>" failing with
84
+ # "role does not exist" on the very next statement.
80
85
  try:
81
86
  if err is None:
82
87
  self.commit(conn)
@@ -84,9 +89,11 @@ class DBInterface(ABC):
84
89
  self.rollback(conn)
85
90
  except Exception as ex:
86
91
  if err is None:
87
- self.log_warning(ex) # error committing. User stuffed up the SQL
92
+ self.log_warning(ex) # commit failed - the transaction did not persist
93
+ return ex
88
94
  else:
89
95
  self.log_critical(ex) # error rolling back. It is serious
96
+ return None
90
97
 
91
98
  def __err_to_exception(self, err, echo):
92
99
  if err is not None:
@@ -102,18 +109,28 @@ class DBInterface(ABC):
102
109
  raise ex
103
110
 
104
111
  def handle_error(self, conn, err, echo=ECHO__none):
105
- self.__attempt_commit_rollback(conn, err)
112
+ commit_err = self.__attempt_commit_rollback(conn, err)
113
+ if err is None and commit_err is not None:
114
+ raise HttpStatusException("Commit failed, transaction not persisted: " + str(commit_err),
115
+ HTTPStatus.INTERNAL_SERVER_ERROR)
106
116
  self.__err_to_exception(err, echo)
107
117
 
108
118
  def put_conn_handle_error(self, conn, err, echo=ECHO__none, skip_rollback_commit: bool = False):
119
+ commit_err = None
109
120
  if not skip_rollback_commit:
110
- self.__attempt_commit_rollback(conn, err)
121
+ commit_err = self.__attempt_commit_rollback(conn, err)
111
122
 
112
123
  try:
113
124
  self.put_conn(conn)
114
125
  except Exception as ex:
115
126
  self.log_warning(ex)
116
127
 
128
+ # A failed commit persisted nothing; surface it (instead of the old silent swallow) so the
129
+ # caller does not treat lost writes as success and hit a misleading error further on.
130
+ if err is None and commit_err is not None:
131
+ raise HttpStatusException("Commit failed, transaction not persisted: " + str(commit_err),
132
+ HTTPStatus.INTERNAL_SERVER_ERROR)
133
+
117
134
  self.__err_to_exception(err, echo)
118
135
 
119
136
  @abstractmethod
@@ -22,6 +22,18 @@ PGCONN__max_conns = 10
22
22
 
23
23
  TIMEOUT = 2.5
24
24
 
25
+ # Upper bound on how long a request waits for the parallel auth verifier's verdict before failing
26
+ # closed. Kept below the nginx proxy read timeout so the worker unwinds and returns its connection
27
+ # rather than the request dying at the proxy while the worker hangs on holding an open transaction.
28
+ WAIT_HOOK__timeout = 30
29
+ ERR__verification_timed_out = "Authorization verification timed out"
30
+
31
+ # libpq TCP keepalives: detect a peer that vanishes (DB reboot, network drop) within ~25s so the
32
+ # socket errors instead of a read blocking indefinitely. Without this the single serial
33
+ # auth-verification thread can wedge forever on a dead connection and, via the wait below, take
34
+ # every authenticated request down with it. Ignored by libpq for unix-socket connections.
35
+ CONN_STR__keepalives = " keepalives=1 keepalives_idle=10 keepalives_interval=5 keepalives_count=3"
36
+
25
37
  ERR__invalid_role = "Role not allowed, invalid format!"
26
38
  ERR__must_use_canned_query = "Must use canned query as you are not an admin!"
27
39
 
@@ -135,6 +147,21 @@ class DBPGInterface(DBInterface):
135
147
  DBPGInterface.HOST_POOLS = {}
136
148
  DBPGInterface.HOST_POOLS_QUEUES = {}
137
149
 
150
+ @staticmethod
151
+ def check_all_pools():
152
+ # Force a synchronous liveness check of every pooled connection, discarding and replacing any
153
+ # that are broken. clean() reboots Postgres, and background activity (the per-minute cron, the
154
+ # auth verifier) can re-open pools DURING the reboot/reinstall window, caching connections to
155
+ # the old postmaster. psycopg_pool only detects those lazily, so the first real request after
156
+ # the wipe (e.g. \register @dba) gets handed a dead connection whose commit then fails "the
157
+ # connection is lost". Calling this once Postgres is stable makes the refresh deterministic.
158
+ for _, user_pool_dict in DBPGInterface.HOST_POOLS.items():
159
+ for _, pool in user_pool_dict.items():
160
+ try:
161
+ pool.check()
162
+ except Exception:
163
+ pass
164
+
138
165
  @staticmethod
139
166
  def _process_returned_conn(username: str, db_name: str, conn, do_reset: bool):
140
167
  if isinstance(conn, JaaqlPGConnection) and conn.jaaql_has_pending_auth():
@@ -199,6 +226,8 @@ class DBPGInterface(DBInterface):
199
226
  if str(port) != "5432":
200
227
  conn_str += " port=" + str(port)
201
228
 
229
+ conn_str += CONN_STR__keepalives
230
+
202
231
  self.conn_str = conn_str
203
232
 
204
233
  if self.db_name not in user_pool:
@@ -309,7 +338,14 @@ class DBPGInterface(DBInterface):
309
338
  do_prepare = prepare and _statement_is_preparable(query)
310
339
 
311
340
  if wait_hook:
312
- res, err, code = wait_hook.get()
341
+ try:
342
+ res, err, code = wait_hook.get(timeout=WAIT_HOOK__timeout)
343
+ except queue.Empty:
344
+ # The parallel verifier never delivered a verdict (its single serial thread
345
+ # wedged, e.g. on a connection killed by a DB reboot). Fail closed and let
346
+ # the worker unwind - without this bound the worker blocks forever holding
347
+ # an open transaction, and enough of them starve the pool into nginx 504s.
348
+ raise Exception(ERR__verification_timed_out)
313
349
  if not res:
314
350
  if code == 500:
315
351
  raise Exception(err)
@@ -1457,6 +1457,11 @@ WHERE
1457
1457
  self.vault.get_obj(VAULT_KEY__jaaql_db_password)
1458
1458
  )
1459
1459
 
1460
+ # Postgres was just rebooted and reinstalled. Discard any connections opened against the old
1461
+ # postmaster during that window (by the cron/verifier/install) so the next request cannot be
1462
+ # handed a dead connection whose commit silently fails. Runs once per wipe - no per-request cost.
1463
+ DBPGInterface.check_all_pools()
1464
+
1460
1465
  def execute_migrations(self, connection: DBInterface):
1461
1466
  self.is_super_admin(connection)
1462
1467
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaaql-middleware-python
3
- Version: 5.3.2
3
+ Version: 5.3.4
4
4
  Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
5
5
  Home-page: https://github.com/JAAQL/JAAQL-middleware-python
6
6
  Author: Software Quality Measurement and Improvement bv