dsmq 0.5.0__py3-none-any.whl → 0.7.0__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.
dsmq/dsmq.py
CHANGED
@@ -5,26 +5,25 @@ import sys
|
|
5
5
|
from threading import Thread
|
6
6
|
import time
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
_default_host = "127.0.0.1"
|
9
|
+
_default_port = 30008
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
_message_length_offset = 1_000_000
|
12
|
+
_header_length = 23
|
13
|
+
_n_retries = 5
|
14
|
+
_first_retry = 0.01 # seconds
|
15
|
+
_time_to_live = 600.0 # seconds
|
15
16
|
|
16
17
|
|
17
|
-
def start_server(host=
|
18
|
+
def start_server(host=_default_host, port=_default_port):
|
18
19
|
"""
|
19
20
|
For best results, start this running in its own process and walk away.
|
20
|
-
|
21
|
-
|
22
21
|
"""
|
23
22
|
sqlite_conn = sqlite3.connect("file:mem1?mode=memory&cache=shared")
|
24
23
|
cursor = sqlite_conn.cursor()
|
25
24
|
|
26
25
|
cursor.execute("""
|
27
|
-
|
26
|
+
CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
|
28
27
|
""")
|
29
28
|
|
30
29
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
@@ -49,7 +48,7 @@ def start_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
|
|
49
48
|
sqlite_conn.close()
|
50
49
|
|
51
50
|
|
52
|
-
def connect_to_server(host=
|
51
|
+
def connect_to_server(host=_default_host, port=_default_port):
|
53
52
|
return DSMQClientSideConnection(host, port)
|
54
53
|
|
55
54
|
|
@@ -59,22 +58,55 @@ class DSMQClientSideConnection:
|
|
59
58
|
self.dsmq_conn.connect((host, port))
|
60
59
|
|
61
60
|
def get(self, topic):
|
62
|
-
|
63
|
-
self.dsmq_conn
|
61
|
+
msg_dict = {"action": "get", "topic": topic}
|
62
|
+
_send_message(self.dsmq_conn, msg_dict)
|
64
63
|
|
65
|
-
|
66
|
-
if
|
64
|
+
msg = _receive_message(self.dsmq_conn)
|
65
|
+
if msg is None:
|
67
66
|
raise RuntimeError("Connection terminated by server")
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
return msg["message"]
|
68
|
+
|
69
|
+
def put(self, topic, msg_body):
|
70
|
+
msg_dict = {"action": "put", "topic": topic, "message": msg_body}
|
71
|
+
_send_message(self.dsmq_conn, msg_dict)
|
72
|
+
|
73
|
+
|
74
|
+
def _send_message(socket_conn, msg_dict):
|
75
|
+
msg = json.dumps(msg_dict)
|
76
|
+
msg_bytes = bytes(msg, "utf-8")
|
77
|
+
n_bytes = len(msg_bytes)
|
78
|
+
|
79
|
+
# Send a header first.
|
80
|
+
# Add a large number to ensure that the message is the same size each time.
|
81
|
+
header_dict = {"msg_length": n_bytes + _message_length_offset}
|
82
|
+
header = json.dumps(header_dict)
|
83
|
+
header_bytes = bytes(header, "utf-8")
|
84
|
+
socket_conn.sendall(header_bytes)
|
74
85
|
|
75
|
-
|
76
|
-
|
77
|
-
|
86
|
+
socket_conn.sendall(msg_bytes)
|
87
|
+
|
88
|
+
|
89
|
+
def _receive_message(socket_conn):
|
90
|
+
# First receive a header
|
91
|
+
header_data = socket_conn.recv(_header_length)
|
92
|
+
if header_data is None:
|
93
|
+
return None
|
94
|
+
if len(header_data) == 0:
|
95
|
+
return None
|
96
|
+
|
97
|
+
header_str = header_data.decode("utf-8")
|
98
|
+
header = json.loads(header_str)
|
99
|
+
msg_length = header["msg_length"] - _message_length_offset
|
100
|
+
|
101
|
+
data = socket_conn.recv(msg_length)
|
102
|
+
if data is None:
|
103
|
+
return None
|
104
|
+
if len(data) == 0:
|
105
|
+
return None
|
106
|
+
|
107
|
+
msg_str = data.decode("utf-8")
|
108
|
+
msg = json.loads(msg_str)
|
109
|
+
return msg
|
78
110
|
|
79
111
|
|
80
112
|
def _handle_client_connection(socket_conn):
|
@@ -86,20 +118,10 @@ def _handle_client_connection(socket_conn):
|
|
86
118
|
time_of_last_purge = time.time()
|
87
119
|
|
88
120
|
while True:
|
89
|
-
|
90
|
-
|
91
|
-
if not data:
|
121
|
+
msg = _receive_message(socket_conn)
|
122
|
+
if msg is None:
|
92
123
|
break
|
93
124
|
|
94
|
-
msg_str = data.decode("utf-8")
|
95
|
-
try:
|
96
|
-
# print("dsmq received ", msg_str)
|
97
|
-
msg = json.loads(msg_str)
|
98
|
-
except json.decoder.JSONDecodeError:
|
99
|
-
print("Message must be json-friendly")
|
100
|
-
print(f" Received: {msg}")
|
101
|
-
continue
|
102
|
-
|
103
125
|
topic = msg["topic"]
|
104
126
|
timestamp = time.time()
|
105
127
|
|
@@ -108,7 +130,7 @@ def _handle_client_connection(socket_conn):
|
|
108
130
|
|
109
131
|
# This block allows for multiple retries if the database
|
110
132
|
# is busy.
|
111
|
-
for i_retry in range(
|
133
|
+
for i_retry in range(_n_retries):
|
112
134
|
try:
|
113
135
|
cursor.execute(
|
114
136
|
"""
|
@@ -119,7 +141,7 @@ VALUES (:timestamp, :topic, :message)
|
|
119
141
|
)
|
120
142
|
sqlite_conn.commit()
|
121
143
|
except sqlite3.OperationalError:
|
122
|
-
wait_time =
|
144
|
+
wait_time = _first_retry * 2**i_retry
|
123
145
|
time.sleep(wait_time)
|
124
146
|
continue
|
125
147
|
break
|
@@ -134,7 +156,7 @@ VALUES (:timestamp, :topic, :message)
|
|
134
156
|
|
135
157
|
# This block allows for multiple retries if the database
|
136
158
|
# is busy.
|
137
|
-
for i_retry in range(
|
159
|
+
for i_retry in range(_n_retries):
|
138
160
|
try:
|
139
161
|
cursor.execute(
|
140
162
|
"""
|
@@ -153,7 +175,7 @@ AND timestamp = a.min_time
|
|
153
175
|
msg,
|
154
176
|
)
|
155
177
|
except sqlite3.OperationalError:
|
156
|
-
wait_time =
|
178
|
+
wait_time = _first_retry * 2**i_retry
|
157
179
|
time.sleep(wait_time)
|
158
180
|
continue
|
159
181
|
break
|
@@ -167,15 +189,14 @@ AND timestamp = a.min_time
|
|
167
189
|
# Handle the case where no results are returned
|
168
190
|
message = ""
|
169
191
|
|
170
|
-
|
171
|
-
socket_conn.sendall(bytes(msg, "utf-8"))
|
192
|
+
_send_message(socket_conn, {"message": message})
|
172
193
|
else:
|
173
194
|
print("Action must either be 'put' or 'get'")
|
174
195
|
|
175
196
|
# Periodically clean out messages from the queue that are
|
176
197
|
# past their sell buy date.
|
177
198
|
# This operation is pretty fast. I clock it at 12 us on my machine.
|
178
|
-
if time.time() - time_of_last_purge >
|
199
|
+
if time.time() - time_of_last_purge > _time_to_live:
|
179
200
|
cursor.execute(
|
180
201
|
"""
|
181
202
|
DELETE FROM messages
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: dsmq
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: A dead simple message queue
|
5
5
|
Requires-Python: >=3.10
|
6
6
|
Description-Content-Type: text/markdown
|
@@ -97,6 +97,8 @@ a queue before it connected.
|
|
97
97
|
- A client will get the oldest message available on a requested topic.
|
98
98
|
Queues are first-in-first-out.
|
99
99
|
|
100
|
+
- Messages older than 600 seconds will be deleted from the queue.
|
101
|
+
|
100
102
|
- Put and get operations are fairly quick--less than 100 $`\mu`$s of processing
|
101
103
|
time plus any network latency--so it can comfortably handle requests at rates of
|
102
104
|
hundreds of times per second. But if you have several clients reading and writing
|
@@ -127,7 +129,7 @@ Connects to an existing message queue server.
|
|
127
129
|
## `DSMQClientSideConnection` class
|
128
130
|
|
129
131
|
This is a convenience wrapper, to make the `get()` and `put()` functions
|
130
|
-
easy to write and remember.
|
132
|
+
easy to write and remember. It's under the hood only, not meant to be called directly.
|
131
133
|
|
132
134
|
### `put(topic, msg)`
|
133
135
|
|
@@ -143,4 +145,4 @@ connected to the server.
|
|
143
145
|
- `topic` (str)
|
144
146
|
- returns str, the content of the message. If there was no eligble message
|
145
147
|
in the topic, or the topic doesn't yet exist,
|
146
|
-
returns ""
|
148
|
+
returns `""`.
|
@@ -1,10 +1,10 @@
|
|
1
1
|
dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
|
2
2
|
dsmq/demo_linux.py,sha256=7yLglGmirDLuuyMxppYSK-dfx2Fg2Q0dIWB4cl2yV1c,622
|
3
|
-
dsmq/dsmq.py,sha256=
|
3
|
+
dsmq/dsmq.py,sha256=f-dR-Pjjn_gDx-EK1evYQWvDq8ypXbhx6DaCWHCInSc,6744
|
4
4
|
dsmq/example_get_client.py,sha256=chFfB2949PBENmgdUc3ASrATq1m4wvHGBzEnOC-o_Xs,296
|
5
5
|
dsmq/example_put_client.py,sha256=mUKCRhmUieMZEpHLFWFvzeKB6IR7A8l4tWN8TvPzdKU,348
|
6
6
|
dsmq/example_server.py,sha256=kkXOPaaTzVxf9_iIM76zU9pZhkPna_1vcGWkPrhCjus,61
|
7
|
-
dsmq-0.
|
8
|
-
dsmq-0.
|
9
|
-
dsmq-0.
|
10
|
-
dsmq-0.
|
7
|
+
dsmq-0.7.0.dist-info/METADATA,sha256=NFG4GQwX9RqIKLR3xvFelzuTVebieS4sJqZ9uFu24Lw,4006
|
8
|
+
dsmq-0.7.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
9
|
+
dsmq-0.7.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
|
10
|
+
dsmq-0.7.0.dist-info/RECORD,,
|
File without changes
|