simo 3.1.3__py3-none-any.whl → 3.1.5__py3-none-any.whl
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.
Potentially problematic release.
This version of simo might be problematic. Click here for more details.
- simo/core/db_backend/base.py +60 -6
- simo/core/management/_hub_template/hub/supervisor.conf +4 -4
- simo/core/management/commands/run_app_mqtt_control.py +18 -2
- simo/core/management/commands/run_app_mqtt_fanout.py +18 -2
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/METADATA +1 -1
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/RECORD +10 -10
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/WHEEL +0 -0
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/entry_points.txt +0 -0
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/licenses/LICENSE.md +0 -0
- {simo-3.1.3.dist-info → simo-3.1.5.dist-info}/top_level.txt +0 -0
simo/core/db_backend/base.py
CHANGED
|
@@ -1,19 +1,73 @@
|
|
|
1
1
|
from django.contrib.gis.db.backends.postgis.base import (
|
|
2
2
|
DatabaseWrapper as PostGisPsycopg2DatabaseWrapper
|
|
3
3
|
)
|
|
4
|
-
|
|
4
|
+
"""Custom PostGIS database wrapper with light self-healing.
|
|
5
|
+
|
|
6
|
+
We retry cursor creation on transient driver-level errors. Previously we
|
|
7
|
+
were catching Django's wrapper exceptions (django.db.utils.InterfaceError/
|
|
8
|
+
OperationalError). However, those are only raised by the outer
|
|
9
|
+
`wrap_database_errors` context. Inside `create_cursor()` the driver
|
|
10
|
+
(`psycopg2`) raises its own exceptions, so our except block never ran and
|
|
11
|
+
connections weren't healed.
|
|
12
|
+
|
|
13
|
+
By catching psycopg2 errors here (in addition to Django's), we ensure
|
|
14
|
+
we can close() and reconnect() this connection when `self.connection`
|
|
15
|
+
is already closed or becomes unusable, avoiding busy error loops in
|
|
16
|
+
MQTT threads and periodic tasks.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from django.db.utils import OperationalError as DjangoOperationalError, InterfaceError as DjangoInterfaceError
|
|
20
|
+
try:
|
|
21
|
+
# Catch driver-level errors where they originate.
|
|
22
|
+
from psycopg2 import OperationalError as PsycopgOperationalError, InterfaceError as PsycopgInterfaceError
|
|
23
|
+
except Exception: # pragma: no cover - very defensive
|
|
24
|
+
PsycopgOperationalError = PsycopgInterfaceError = Exception
|
|
5
25
|
from django.utils.asyncio import async_unsafe
|
|
26
|
+
import time
|
|
27
|
+
import random
|
|
6
28
|
|
|
7
29
|
|
|
8
30
|
class DatabaseWrapper(PostGisPsycopg2DatabaseWrapper):
|
|
9
31
|
@async_unsafe
|
|
10
32
|
def create_cursor(self, name=None):
|
|
33
|
+
"""Create a DB cursor with a single, simple heal-once path.
|
|
34
|
+
|
|
35
|
+
- Fast path: return cursor immediately.
|
|
36
|
+
- On error: if it's a connectivity issue (known exceptions or
|
|
37
|
+
connection.closed set), close + backoff + reconnect, then retry once.
|
|
38
|
+
- Otherwise: re-raise the original exception.
|
|
39
|
+
"""
|
|
11
40
|
try:
|
|
12
41
|
return super().create_cursor(name=name)
|
|
13
|
-
except
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
42
|
+
except Exception as e:
|
|
43
|
+
# Determine if this is a connectivity problem
|
|
44
|
+
is_connectivity_err = isinstance(
|
|
45
|
+
e,
|
|
46
|
+
(
|
|
47
|
+
DjangoInterfaceError,
|
|
48
|
+
DjangoOperationalError,
|
|
49
|
+
PsycopgInterfaceError,
|
|
50
|
+
PsycopgOperationalError,
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
if not is_connectivity_err:
|
|
54
|
+
try:
|
|
55
|
+
conn = getattr(self, 'connection', None)
|
|
56
|
+
is_connectivity_err = bool(getattr(conn, 'closed', 0))
|
|
57
|
+
except Exception:
|
|
58
|
+
is_connectivity_err = False
|
|
18
59
|
|
|
60
|
+
if not is_connectivity_err:
|
|
61
|
+
# Not a connection issue; bubble up unchanged
|
|
62
|
+
raise
|
|
19
63
|
|
|
64
|
+
# Heal this very connection and retry once
|
|
65
|
+
try:
|
|
66
|
+
self.close()
|
|
67
|
+
finally:
|
|
68
|
+
try:
|
|
69
|
+
time.sleep(0.05 + random.random() * 0.15) # 50–200 ms
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
self.connect()
|
|
73
|
+
return super().create_cursor(name=name)
|
|
@@ -81,12 +81,12 @@ stopsignal=INT
|
|
|
81
81
|
environment=PYTHONUNBUFFERED=1
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
[program:simo-
|
|
84
|
+
[program:simo-mqtt-control]
|
|
85
85
|
directory={{ project_dir }}/hub/
|
|
86
86
|
command={{ venv_path }}/python manage.py run_app_mqtt_control
|
|
87
87
|
process_name=%(program_name)s
|
|
88
88
|
user=root
|
|
89
|
-
stdout_logfile=/var/log/simo/
|
|
89
|
+
stdout_logfile=/var/log/simo/mqtt_control.log
|
|
90
90
|
stdout_logfile_maxbytes=1MB
|
|
91
91
|
stdout_logfile_backups=3
|
|
92
92
|
redirect_stderr=true
|
|
@@ -97,12 +97,12 @@ killasgroup=true
|
|
|
97
97
|
stopsignal=INT
|
|
98
98
|
environment=PYTHONUNBUFFERED=1
|
|
99
99
|
|
|
100
|
-
[program:simo-
|
|
100
|
+
[program:simo-mqtt-fanout]
|
|
101
101
|
directory={{ project_dir }}/hub/
|
|
102
102
|
command={{ venv_path }}/python manage.py run_app_mqtt_fanout
|
|
103
103
|
process_name=%(program_name)s
|
|
104
104
|
user=root
|
|
105
|
-
stdout_logfile=/var/log/simo/
|
|
105
|
+
stdout_logfile=/var/log/simo/mqtt_fanout.log
|
|
106
106
|
stdout_logfile_maxbytes=1MB
|
|
107
107
|
stdout_logfile_backups=3
|
|
108
108
|
redirect_stderr=true
|
|
@@ -21,10 +21,18 @@ class Command(BaseCommand):
|
|
|
21
21
|
client.username_pw_set('root', settings.SECRET_KEY)
|
|
22
22
|
client.on_connect = self.on_connect
|
|
23
23
|
client.on_message = self.on_message
|
|
24
|
+
client.on_disconnect = self.on_disconnect
|
|
25
|
+
# Back off on reconnects to avoid busy-spin during outages
|
|
26
|
+
client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
27
|
+
# Route Paho logs to Python logging for visibility
|
|
28
|
+
try:
|
|
29
|
+
client.enable_logger()
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
24
32
|
client.connect(host=settings.MQTT_HOST, port=settings.MQTT_PORT)
|
|
25
33
|
try:
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
# Blocking network loop with built-in reconnect/backoff
|
|
35
|
+
client.loop_forever(retry_first_connection=True)
|
|
28
36
|
finally:
|
|
29
37
|
try:
|
|
30
38
|
client.disconnect()
|
|
@@ -118,6 +126,14 @@ class Command(BaseCommand):
|
|
|
118
126
|
# Never crash the consumer
|
|
119
127
|
pass
|
|
120
128
|
|
|
129
|
+
def on_disconnect(self, client, userdata, rc):
|
|
130
|
+
# Non-zero rc means unexpected disconnect. Paho will back off and retry.
|
|
131
|
+
if rc != 0:
|
|
132
|
+
try:
|
|
133
|
+
print(f"Control MQTT disconnect rc={rc}; reconnecting with backoff...", file=sys.stderr)
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
136
|
+
|
|
121
137
|
def respond(self, client, user_id, request_id, ok=True, result=None, error=None):
|
|
122
138
|
if not request_id:
|
|
123
139
|
return
|
|
@@ -21,10 +21,18 @@ class Command(BaseCommand):
|
|
|
21
21
|
self.client.username_pw_set('root', settings.SECRET_KEY)
|
|
22
22
|
self.client.on_connect = self.on_connect
|
|
23
23
|
self.client.on_message = self.on_message
|
|
24
|
+
self.client.on_disconnect = self.on_disconnect
|
|
25
|
+
# Back off on reconnects to avoid busy-spin during outages
|
|
26
|
+
self.client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
27
|
+
# Route Paho logs to Python logging for visibility
|
|
28
|
+
try:
|
|
29
|
+
self.client.enable_logger()
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
24
32
|
self.client.connect(host=settings.MQTT_HOST, port=settings.MQTT_PORT)
|
|
25
33
|
try:
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
# Blocking network loop with built-in reconnect/backoff
|
|
35
|
+
self.client.loop_forever(retry_first_connection=True)
|
|
28
36
|
finally:
|
|
29
37
|
try:
|
|
30
38
|
self.client.disconnect()
|
|
@@ -95,3 +103,11 @@ class Command(BaseCommand):
|
|
|
95
103
|
except Exception:
|
|
96
104
|
# Never crash the consumer
|
|
97
105
|
print('Fanout error:', ''.join(traceback.format_exception(*sys.exc_info())), file=sys.stderr)
|
|
106
|
+
|
|
107
|
+
def on_disconnect(self, client, userdata, rc):
|
|
108
|
+
# Non-zero rc means unexpected disconnect. Paho will back off and retry.
|
|
109
|
+
if rc != 0:
|
|
110
|
+
try:
|
|
111
|
+
print(f"Fanout MQTT disconnect rc={rc}; reconnecting with backoff...", file=sys.stderr)
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
@@ -191,7 +191,7 @@ simo/core/__pycache__/views.cpython-38.pyc,sha256=kYKvEcyKicdkTcN0iEJ4pT101-KHiZ
|
|
|
191
191
|
simo/core/__pycache__/widgets.cpython-312.pyc,sha256=f_AiYFfA7Wl4Wm9FqUOlluSGLAN_nkLyDUsKpkScsXg,4447
|
|
192
192
|
simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
|
|
193
193
|
simo/core/db_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
194
|
-
simo/core/db_backend/base.py,sha256=
|
|
194
|
+
simo/core/db_backend/base.py,sha256=fGagw119bvP3Ns3NFvIDlrlNSa9-LLt2HMuIjnB9h9g,2933
|
|
195
195
|
simo/core/db_backend/__pycache__/__init__.cpython-312.pyc,sha256=Brq4UBCggJrt590OFV3VmgARJYRK902FnnFFFK_Azng,133
|
|
196
196
|
simo/core/db_backend/__pycache__/__init__.cpython-38.pyc,sha256=sxC6PmFqnwe6RRKzSR7QtUFzRc_aRRR1fffsR3TPLRc,169
|
|
197
197
|
simo/core/db_backend/__pycache__/base.cpython-312.pyc,sha256=ykKrTOC28v2cl0C--bqeLEaydHS5CG1RFGL0lon53HU,1135
|
|
@@ -286,7 +286,7 @@ simo/core/management/_hub_template/hub/celeryc.py,sha256=3ksDXftIZKJ4Cq9WNKJERdZ
|
|
|
286
286
|
simo/core/management/_hub_template/hub/manage.py,sha256=PNNlw3EVeIJDgkG0l-klqoxsKWfTYWG9jzRG0upmAaI,620
|
|
287
287
|
simo/core/management/_hub_template/hub/nginx.conf,sha256=_-ch60oQYXMWmcvYUEbxMvXq9S46exwKmiBaVrxkQ3c,2339
|
|
288
288
|
simo/core/management/_hub_template/hub/settings.py,sha256=4QhvhbtLRxHvAntwqG_qeAAtpDUqKvN4jzw9u3vqff8,361
|
|
289
|
-
simo/core/management/_hub_template/hub/supervisor.conf,sha256=
|
|
289
|
+
simo/core/management/_hub_template/hub/supervisor.conf,sha256=g9Df8scz-FrcLJWSkFzwcEH9yfGR332W_BWXCxxpR_U,3647
|
|
290
290
|
simo/core/management/_hub_template/hub/urls.py,sha256=Ydm-1BkYAzWeEF-MKSDIFf-7aE4qNLPm48-SA51XgJQ,25
|
|
291
291
|
simo/core/management/_hub_template/hub/wsgi.py,sha256=Lo-huLHnMDTxSmMBOodVFMWBls9poddrV2KRzXU0xGo,280
|
|
292
292
|
simo/core/management/_hub_template/hub/__pycache__/asgi.cpython-312.pyc,sha256=FVjb5whNF2mVopSz71s0Zms8d_b90_4smf84OWWkzDI,396
|
|
@@ -299,8 +299,8 @@ simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
|
|
|
299
299
|
simo/core/management/commands/gateways_manager.py,sha256=oHzgC-eV4w_KHkiz6eCAlt3XMIwtS8_7mHDQ4UsU5y0,6967
|
|
300
300
|
simo/core/management/commands/on_http_start.py,sha256=kxtWB3lOSyQA8tVmZRmLmaoVsliy5mhZp9_wwCvkj_A,6279
|
|
301
301
|
simo/core/management/commands/republish_mqtt_state.py,sha256=mc8e7qnhDyC9_fiYr6d0g2s_3wGUXCrLo95-HBIrkOA,2248
|
|
302
|
-
simo/core/management/commands/run_app_mqtt_control.py,sha256
|
|
303
|
-
simo/core/management/commands/run_app_mqtt_fanout.py,sha256=
|
|
302
|
+
simo/core/management/commands/run_app_mqtt_control.py,sha256=-eDDAXbGtjqN1OK7Ym2FIdP1wVNJYR0DdvIyRzmXPDc,5592
|
|
303
|
+
simo/core/management/commands/run_app_mqtt_fanout.py,sha256=diSEZM_sBQqm4wzzVgBT90Kt8kAhbZm5at2meEMv73M,4605
|
|
304
304
|
simo/core/management/commands/run_gateway.py,sha256=bp0FQQoBeOSoxjHCCMicDL1fxPZZGyLgnq2QKht3bJo,645
|
|
305
305
|
simo/core/management/commands/__pycache__/__init__.cpython-312.pyc,sha256=2Rw8IQ8vlr-wLXgjtc9zACkDD_SWZCxDlbrXw5vfI6o,142
|
|
306
306
|
simo/core/management/commands/__pycache__/__init__.cpython-38.pyc,sha256=WKpfZZpAB9D7U4X6oWQIrU_H-6rUmq8Gl9fj9XaY2fw,178
|
|
@@ -11034,9 +11034,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
11034
11034
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11035
11035
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11036
11036
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11037
|
-
simo-3.1.
|
|
11038
|
-
simo-3.1.
|
|
11039
|
-
simo-3.1.
|
|
11040
|
-
simo-3.1.
|
|
11041
|
-
simo-3.1.
|
|
11042
|
-
simo-3.1.
|
|
11037
|
+
simo-3.1.5.dist-info/licenses/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
11038
|
+
simo-3.1.5.dist-info/METADATA,sha256=hXkJ2qxHLFHebhXd3yRy4Gvj2Bd4Vi2-ulumfeyYcIM,2224
|
|
11039
|
+
simo-3.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11040
|
+
simo-3.1.5.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
|
|
11041
|
+
simo-3.1.5.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
11042
|
+
simo-3.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|