rabbitmq_plugin plugin
The rabbitmq_plugin plugin is designed to register events in the Firebird database and send them to the RabbitMQ queue server.
Each event is a JSON document.
The event contains the following information:
- event type (
INSERT,UPDATE,DELETE,SET SEQUENCE,EXECUTE SQL); - for
INSERT,UPDATE,DELETEevents:- the name of the table for which this event occurred;
- the number of the transaction in which this event occurred;
- old and new values of the table record fields;
- an array of changed fields for the
UPDATEevent; - new values of BLOB fields;
- for the
SET SEQUENCEevent:- sequence (generator) name;
- sequence value;
- for the
EXECUTE SQLevent:- the number of the transaction in which this event occurred;
- SQL query text.
Only new field values are present in the INSERT event.
The UPDATE event contains both old and new field values.
Only the old field values are present in the DELETE event.
The EXECUTE SQL event occurs only for DDL queries.
Note: for BLOB fields, the BlobId is always stored. If the configuration parameter is dumpBlobs, then in addition to this for events INSERT and UPDATE will publish new BLOB field values. For old field values, this is not possible because old field BLOB values are not present in the replication log files.
The algorithm of the rabbitmq_plugin plugin
Each event is packaged in JSON format and sent to the RabbitMQ queue server exchange specified in the exchange_name parameter.
Depending on the exchanger type, the event is either sent to all queues associated with the exchanger, or to the queue bound by the routing_key routing key.
If the queue_name parameter is specified, then the queue with the given name is declared and bound by the routing_key key to the exchanger exchange_name.
- the
fb_streamingservice analyzes the replication log directory set for the task for unprocessed files; - for unprocessed replication files, the following events are analyzed:
- start of transaction. Allocates a structure in memory to store savepoints;
- create a savepoint. A structure in memory is allocated for storing documents;
- release the savepoint. Documents saved in this savepoint are copied to a higher-level savepoint;
- rollback of the savepoint (the savepoint is destroyed along with all documents);
- commit the transaction. Documents created in the
INSERT,UPDATE,DELETEandEXECUTE SQLevents are sent to the RabbitMQ exchanger; - transaction rollback. All savepoints and documents stored in them are destroyed;
- DDL query execution (
EXECUTE SQL). A new document is created and saved to the list of documents for the savepoint. The statement text, event type, and transaction number are stored in the document; - setting a new sequence value (
SET SEQUENCE). A new document is created and immediately sent to the RabbitMQ exchange. The sequence name, event type, and new sequence value are stored in the document; INSERT,UPDATEandDELETEevents. The table name is checked in theinclude_tablesandexclude_tablesfilters, and if the table name satisfies the filtering criteria, then a new document is created and saved to the list of documents for the savepoint. The document saves the table name, event type, transaction number, old and new field values.
- after processing the file, its number is fixed in the control file.
Other replication events are ignored.
Setting up the rabbitmq_plugin plugin
First of all, you need to set up asynchronous replication for your database, for this you need to add the following lines to the replication.conf file:
database = d:\fbdata\4.0\horses.fdb
{
journal_directory = d:\fbdata\4.0\replication\horses\journal
journal_archive_directory = d:\fbdata\4.0\replication\horses\archive
journal_archive_command = "copy $(pathname) $(archivepathname) && copy $(pathname) d:\fbdata\4.0\replication\horses\archive_rabbit
}
Please note: that there is a duplication of log archive files so that logical replication and the task of sending events to RabbitMQ work at the same time. This is necessary because log archive files are deleted after processing and cannot be used by another task.
Now you need to enable replication in the database:
ALTER DATABASE INCLUDE ALL TO PUBLICATION;
ALTER DATABASE ENABLE PUBLICATION;
Next, configure the fb_streaming.conf configuration so that fb_streaming automatically sends events to the RabbitMQ exchanger.
Before configuring, open the “RabbitMQ Management” tool in your browser. If you installed it on the same computer, then by default you need to type in the browser http://localhost:15672, login “guest” password “guest”. This tool allows you to manage exchanges, queues and bindings, as well as view queues.
task = d:\fbdata\4.0\replication\horses\archive_rabbit
{
database = inet://localhost:3054/d:\fbdata\4.0\horses.fdb
username = SYSDBA
password = masterkey
deleteProcessedFile = true
plugin = rabbitmq_plugin
dumpBlobs = true
register_ddl_events = true
register_sequence_events = true
# include_tables =
# exclude_tables =
rabbit_uri = amqp://guest:guest@localhost
exchange_name = horses
exchange_type = direct
queue_name = horses_events
routing_key = 123
}
On Linux, this configuration will look like this:
task = /mnt/d/fbdata/4.0/replication/horses/archive
{
database = inet://192.168.1.48:3054/horses
username = SYSDBA
password = masterkey
deleteProcessedFile = true
plugin = rabbitmq_plugin
dumpBlobs = true
register_ddl_events = true
register_sequence_events = true
# include_tables =
# exclude_tables =
rabbit_uri = amqp://test:[email protected]
exchange_name = horses
exchange_type = direct
queue_name = horses_events
routing_key = 123
}
There are a number of additional settings for this plugin:
rabbit_uri- URI to connect to the RabbitMQ server;exchange_name- exchanger name. If the exchanger does not yet exist, then it will be created;exchange_type- exchanger type. Valid values:fanout,topic,direct(defaultfanout);queue_name- queue name. If specified, the queue is declared and bound to the exchanger by the keyrouting_key;routing_key- routing key;dumpBlobs- whether to publish new values of BLOB fields (falseby default);register_ddl_events- whether to register DDL events (trueby default);register_sequence_events- whether to register sequence value setting events (trueby default);include_tables- a regular expression that defines the names of tables for which it is necessary to track events;exclude_tables- a regular expression that defines the names of tables for which events should not be tracked;charset_for_none_fields- character set used for converting data from string fields with the character set typeNONE. Typically, data in string fields with theNONEcharacter set is stored “as is,” meaning in the original encoding used by the application when saving it. Firebird does not re-encode such strings. The JSON format requires all strings to be inUTF-8encoding. To correctly convert strings fromNONEfields toUTF8, the system needs to know the original encoding of the data. This is precisely what thecharset_for_none_fieldsparameter specifies.
Now you can install and start the service:
fb_streaming install
fb_streaming start
On Linux:
sudo systemctl enable fb_streaming
sudo systemctl start fb_streaming
While the service is running, the horses_events queue will receive messages.
The content of the messages will be something like this:
{
"event": "EXECUTE SQL",
"sql": "CREATE SEQUENCE SEQ1",
"tnx": 6590
}
{
"event": "EXECUTE SQL",
"sql": "CREATE TABLE TABLE1 (\r\n ID INT NOT NULL,\r\n S VARCHAR(10),\r\n PRIMARY KEY(ID)\r\n)",
"tnx": 6591
}
{
"event": "EXECUTE SQL",
"sql": "ALTER TABLE TABLE1\r\nENABLE PUBLICATION",
"tnx": 6594
}
{
"event": "SET SEQUENCE",
"sequence": "SEQ1",
"value": 1
}
{
"event": "INSERT",
"table": "TABLE1",
"tnx": 6597,
"record": {
"ID": 1,
"S": "Hello"
}
}
{
"event": "UPDATE",
"table": "COLOR",
"tnx": 11771,
"changedFields": [ "NAME_DE" ],
"oldRecord": {
"NAME_EN": "dun",
"NAME": "dun",
"CODE_COLOR": 14,
"CODE_SENDER": 1,
"NAME_DE": "",
"SHORTNAME_EN": "dun",
"SHORTNAME": "d."
},
"record": {
"NAME_EN": "dun",
"NAME": "dun",
"CODE_COLOR": 14,
"CODE_SENDER": 1,
"NAME_DE": "g",
"SHORTNAME_EN": "dun",
"SHORTNAME": "d."
}
}
{
"event": "INSERT",
"table": "CLIP",
"tnx": 11821,
"record": {
"AVALUE": 44,
"CODE_CLIP": 1,
"CODE_CLIPTYPE": 1,
"CODE_RECORD": 345,
"REMARK": null
}
}
{
"event": "DELETE",
"table": "CLIP",
"tnx": 11849,
"record": {
"AVALUE": 44,
"CODE_CLIP": 1,
"CODE_CLIPTYPE": 1,
"CODE_RECORD": 345,
"REMARK": null
}
}
{
"event": "UPDATE",
"table": "BREED",
"tnx": 11891,
"changedFields": [ "MARK" ],
"oldRecord": {
"NAME": "Orlov trotter",
"CODE_DEPARTURE": 15,
"CODE_BREED": 55,
"CODE_SENDER": 1,
"NAME_EN": "Orlov trotter",
"SHORTNAME_EN": "orl. trot.",
"SHORTNAME": "orl.trot.",
"MARK": ""
},
"record": {
"NAME": "Orlov trotter",
"CODE_DEPARTURE": 15,
"CODE_BREED": 55,
"CODE_SENDER": 1,
"NAME_EN": "Orlov trotter",
"SHORTNAME_EN": "orl. trot.",
"SHORTNAME": "orl.trot.",
"MARK": "5"
}
}
Field descriptions:
event- event type;table- the name of the table for which the event occurred;tnx- transaction number in which the event occurred;record- a new record in theINSERTandUPDATEevents, an old one in theDELETEevent;oldRecord- old record in theUPDATEevent;changedFields- list of column names that were changed in theUPDATEevent;newBlobs- new values of BLOB fields;sql- SQL query text for DDL operators;sequence- name of the sequence;value- new value of the sequence.