1
1
# message-db-py
2
2
3
+ Message DB is a fully-featured event store and message store implemented in
4
+ PostgreSQL for Pub/Sub, Event Sourcing, Messaging, and Evented Microservices
5
+ applications.
6
+
3
7
` message-db-py ` is a Python interface to the Message DB event store and message
4
8
store, designed for easy integration into Python applications.
5
9
@@ -17,24 +21,350 @@ Use pip to install:
17
21
$ pip install message-db-py
18
22
```
19
23
24
+ ## Setting up Message DB database
25
+
26
+ Clone the Message DB repository to set up the database:
27
+
28
+ ``` shell
29
+ git clone
[email protected] :message-db/message-db.git
30
+ ```
31
+
32
+ More detailed instructions are in the [ Installation]
33
+ (https://github.com/message-db/message-db?tab=readme-ov-file#installation )
34
+ section of Message DB repo.
35
+
36
+ Running the database installation script creates the database, schema, table,
37
+ indexes, functions, views, types, a user role, and limit the user's privileges
38
+ to the message store's public interface.
39
+
40
+ The installation script is in the database directory of the cloned Message DB
41
+ repo. Change directory to the message-db directory where you cloned the repo,
42
+ and run the script:
43
+
44
+ ``` shell
45
+ database/install.sh
46
+ ```
47
+
48
+ Make sure that your default Postgres user has administrative privileges.
49
+
50
+ ### Database Name
51
+
52
+ By default, the database creation tool will create a database named
53
+ ` message_store ` .
54
+
55
+ If you prefer either a different database name, you can override the name
56
+ using the ` DATABASE_NAME ` environment variable.
57
+
58
+ ``` shell
59
+ DATABASE_NAME=some_other_database database/install.sh
60
+ ```
61
+
62
+ ### Uninstalling the Database
63
+
64
+ If you need to drop the database (for example, on a local dev machine):
65
+
66
+ ``` bash
67
+ database/uninstall.sh
68
+ ```
69
+
70
+ If you're upgrading a previous version of the database:
71
+
72
+ ``` bash
73
+ database/update.sh
74
+ ```
75
+
76
+ ## Docker Image
77
+
78
+ You can optionally use a Docker image with Message DB pre-installed and ready
79
+ to go. This is especially helpful to run test cases locally.
80
+
81
+ The docker image is available in [ Docker Hub] ( https://hub.docker.com/r/ethangarofolo/message-db ) .
82
+ The source is in [ Gitlab] ( https://gitlab.com/such-software/message-db-docker )
83
+
20
84
## Usage
21
85
86
+ The complete user guide for Message DB is available at
87
+ [ http://docs.eventide-project.org/user-guide/message-db/ ]
88
+ (http://docs.eventide-project.org/user-guide/message-db/ ).
89
+
90
+ Below is documentation for methods exposed through the Python API.
91
+
92
+ ### Quickstart
93
+
22
94
Here's a quick example of how to publish and read messages using Message-DB-py:
23
95
24
96
``` python
25
- from message_db.client import MessageDB
97
+ from message_db import MessageDB
26
98
27
99
# Initialize the database connection
28
- mdb = MessageDB(" your_connection_string " )
100
+ store = MessageDB(CONN_URL )
29
101
30
102
# Write a message
31
- mdb .write(" your_stream_name " , " your_message_type " , {" data " : " value " })
103
+ store .write(" user_stream " , " register " , {" name " : " John Doe " })
32
104
33
105
# Read a message
34
- message = mdb .read_last_message(" your_stream_name " )
106
+ message = store .read_last_message(" user_stream " )
35
107
print (message)
36
108
```
37
109
110
+ ## Primary APIs
111
+
112
+ ### Write messages
113
+
114
+ The ` write ` method is used to append a new message to a specified stream within
115
+ the message database. This method ensures that the message is written with the
116
+ appropriate type, data, and metadata, and optionally, at a specific expected
117
+ version of the stream.
118
+
119
+ #### Parameters
120
+
121
+ - ` stream_name ` (` str ` ): The name of the stream to which the message will be
122
+ written. This identifies the logical series of messages.
123
+ - ` message_type ` (` str ` ): The type of message being written. Typically, this
124
+ reflects the nature of the event or data change the message represents.
125
+ - ` data ` (` Dict ` ): The data payload of the message. This should be a dictionary
126
+ containing the actual information the message carries.
127
+ - ` metadata ` (` Dict ` | ` None ` ): Optional. Metadata about the message, provided as a
128
+ dictionary. Metadata can include any additional information that is not part of
129
+ the - main data payload, such as sender information or timestamps.
130
+ Defaults to None.
131
+ - ` expected_version ` (` int ` | ` None ` ): Optional. The version of the stream where the
132
+ client expects to write the message. This is used for concurrency control and
133
+ ensuring the integrity of the stream's order. Defaults to ` None ` .
134
+
135
+ #### Returns
136
+
137
+ - ` position ` (` int ` ): The position (or version number) of the message in the
138
+ stream after it has been successfully written.
139
+
140
+ #### Example
141
+
142
+ ``` python
143
+ message_db = MessageDB(connection_pool = my_pool)
144
+ stream_name = " user_updates"
145
+ message_type = " UserCreated"
146
+ data = {" user_id" : 123 , " username" : " example" }
147
+ metadata = {" source" : " web_app" }
148
+
149
+ position = message_db.write(stream_name, message_type, data, metadata)
150
+
151
+ print (" Message written at position:" , position)
152
+ ```
153
+
154
+ ### Read messages from a stream or category
155
+
156
+ The ` read ` method retrieves messages from a specified stream or category. This
157
+ method supports flexible query options through a direct SQL parameter or by
158
+ determining the SQL based on the stream name and its context
159
+ (stream vs. category vs. all messages).
160
+
161
+ #### Parameters
162
+
163
+ - ` stream_name ` (` str ` ): The identifier for the stream or category from which
164
+ messages are to be retrieved. Special names like "$all" can be used to fetch
165
+ messages across all streams.
166
+ - ` sql ` (` str ` | ` None ` , optional): An optional SQL query string that if
167
+ provided, overrides the default SQL generation based on the stream_name.
168
+ If None, the SQL is automatically generated based on the stream_name value.
169
+ Defaults to None.
170
+ - ` position ` (` int ` , optional): The starting position in the stream or category
171
+ from which to begin reading messages. Defaults to 0.
172
+ - ` no_of_messages ` (` int ` , optional): The maximum number of messages to
173
+ retrieve. Defaults to 1000.
174
+
175
+ #### Returns
176
+
177
+ - List[ Dict[ str, Any]] : A list of messages, where each message is
178
+ represented as a dictionary containing details such as the message ID,
179
+ stream name, type, position, global position, data, metadata, and timestamp.
180
+
181
+ #### Example
182
+
183
+ ``` python
184
+ message_db = MessageDB(connection_pool = my_pool)
185
+ stream_name = " user-updates"
186
+ position = 10
187
+ no_of_messages = 50
188
+
189
+ # Reading from a specific stream
190
+ messages = message_db.read(stream_name, position = position, no_of_messages = no_of_messages)
191
+
192
+ # Custom SQL query
193
+ custom_sql = " SELECT * FROM get_stream_messages(%(stream_name)s , %(position)s , %(batch_size)s );"
194
+ messages = message_db.read(stream_name, sql = custom_sql, position = position, no_of_messages = no_of_messages)
195
+
196
+ for message in messages:
197
+ print (message)
198
+ ```
199
+
200
+ ### Read Last Message from stream
201
+
202
+ The ` read_last_message ` method retrieves the most recent message from a
203
+ specified stream. This method is useful when you need the latest state or
204
+ event in a stream without querying the entire message history.
205
+
206
+ #### Parameters
207
+
208
+ - ` stream_name ` (` str ` ): The name of the stream from which the last message is to be
209
+ retrieved.
210
+
211
+ #### Returns
212
+
213
+ - ` Dict ` [ ` str ` , ` Any ` ] | ` None ` : A dictionary representing the last message
214
+ in the specified stream. If the stream is empty or the message does not exist,
215
+ ` None ` is returned.
216
+
217
+ #### Example
218
+
219
+ ``` python
220
+ message_db = MessageDB(connection_pool = my_pool)
221
+ stream_name = " user_updates"
222
+
223
+ # Reading the last message from a stream
224
+ last_message = message_db.read_last_message(stream_name)
225
+
226
+ if last_message:
227
+ print (" Last message data:" , last_message)
228
+ else :
229
+ print (" No messages found in the stream." )
230
+ ```
231
+
232
+ ## Utility APIs
233
+
234
+ ### Read Stream
235
+
236
+ The ` read_stream ` method retrieves a sequence of messages from a specified stream
237
+ within the message database. This method is specifically designed to fetch
238
+ messages from a well-defined stream based on a starting position and a
239
+ specified number of messages.
240
+
241
+ #### Parameters
242
+
243
+ - ` stream_name ` (` str ` ): The name of the stream from which messages are to be
244
+ retrieved. This name must include a hyphen (-) to be recognized as a valid
245
+ stream identifier.
246
+ - ` position ` (` int ` , optional): The zero-based index position from which to start
247
+ reading messages. Defaults to 0, which starts reading from the beginning of
248
+ the stream.
249
+ - ` no_of_messages ` (` int ` , optional): The maximum number of messages to retrieve
250
+ from the stream. Defaults to 1000.
251
+
252
+ #### Returns
253
+
254
+ - ` List ` [ ` Dict ` [ ` str ` , ` Any ` ]] : A list of dictionaries, each representing a message
255
+ retrieved from the stream. Each dictionary contains the message details
256
+ structured in key-value pairs.
257
+
258
+ #### Exceptions
259
+
260
+ - ` ValueError ` : Raised if the provided stream_name does not contain a hyphen
261
+ (-), which is required to validate the name as a stream identifier.
262
+
263
+ #### Example
264
+
265
+ ``` python
266
+ message_db = MessageDB(connection_pool = my_pool)
267
+ stream_name = " user-updates-2023"
268
+ position = 0
269
+ no_of_messages = 100
270
+
271
+ messages = message_db.read_stream(stream_name, position, no_of_messages)
272
+
273
+ for message in messages:
274
+ print (message)
275
+ ```
276
+
277
+ ### Read Category
278
+
279
+ The ` read_category ` method retrieves a sequence of messages from a specified
280
+ category within the message database. It is designed to fetch messages based
281
+ on a category identifier, starting from a specific position, and up to a
282
+ defined limit of messages.
283
+
284
+ #### Parameters
285
+
286
+ - ` category_name ` (` str ` ): The name of the category from which messages are to be
287
+ retrieved. This identifier should not include a hyphen (-) to validate it as
288
+ a category name.
289
+ - ` position ` (` int ` , optional): The zero-based index position from which to start
290
+ reading messages within the category. Defaults to 0.
291
+ - ` no_of_messages ` (` int ` , optional): The maximum number of messages to retrieve
292
+ from the category. Defaults to 1000.
293
+
294
+ #### Returns
295
+
296
+ - List[ Dict[ str, Any]] : A list of dictionaries, each representing a message.
297
+ Each dictionary includes details about the message such as the message ID,
298
+ stream name, type, position, global position, data, metadata, and time of
299
+ creation.
300
+
301
+ #### Exceptions
302
+
303
+ - ` ValueError ` : Raised if the provided category_name contains a hyphen (-),
304
+ which is not allowed for category identifiers and implies a misunderstanding
305
+ between streams and categories.
306
+
307
+ #### Example
308
+
309
+ ``` python
310
+ message_db = MessageDB(connection_pool = my_pool)
311
+ category_name = " user_updates"
312
+ position = 0
313
+ no_of_messages = 100
314
+
315
+ # Reading messages from a category
316
+ messages = message_db.read_category(category_name, position, no_of_messages)
317
+
318
+ for message in messages:
319
+ print (message)
320
+ ```
321
+
322
+ ### Write Batch
323
+
324
+ The ` write_batch ` method is designed to write a series of messages to a
325
+ specified stream in a batch operation. It ensures atomicity in writing
326
+ operations, where all messages are written in sequence, and each subsequent
327
+ message can optionally depend on the position of the last message written.
328
+ This method is useful when multiple messages need to be written as a part of a
329
+ single transactional context.
330
+
331
+ #### Parameters
332
+
333
+ - ` stream_name ` (` str ` ): The name of the stream to which the batch of messages
334
+ will be written.
335
+ - ` data ` (` List ` [ ` Tuple ` [ ` str ` , ` Dict ` , ` Dict ` | ` None ` ]] ): A list of tuples,
336
+ where each tuple represents a message. The tuple format is (message_type, data,
337
+ metadata), with metadata being optional.
338
+ - ` expected_version ` (` int ` | ` None ` , optional): The version of the stream
339
+ where the batch operation expects to start writing. This can be used for
340
+ concurrency control to ensure messages are written in the expected order.
341
+ Defaults to None.
342
+
343
+ #### Returns
344
+
345
+ - ` position ` (` int ` ): The position (or version number) of the last message
346
+ written in the stream as a result of the batch operation.
347
+
348
+ #### Example
349
+
350
+ ``` python
351
+ message_db = MessageDB(connection_pool = my_pool)
352
+ stream_name = " order_events"
353
+ data = [
354
+ (" OrderCreated" , {" order_id" : 123 , " product_id" : 456 }, None ),
355
+ (" OrderShipped" ,
356
+ {" order_id" : 123 , " shipment_id" : 789 },
357
+ {" priority" : " high" }
358
+ ),
359
+ (" OrderDelivered" , {" order_id" : 123 , " delivery_date" : " 2024-04-23" }, None )
360
+ ]
361
+
362
+ # Writing a batch of messages to a stream
363
+ last_position = message_db.write_batch(stream_name, data)
364
+
365
+ print (f " Last message written at position: { last_position} " )
366
+ ```
367
+
38
368
## License
39
369
40
- The Postgres Message Store is released under the [ MIT License ] ( https://github.com/subhashb/message-db-py/blob/main/LICENSE ) .
370
+ [ MIT] ( https://github.com/subhashb/message-db-py/blob/main/LICENSE )
0 commit comments