From ddff95024c02894a7d563abe46140174ccae049a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 23 Jan 2026 09:19:49 +0100 Subject: [PATCH 01/35] Client config (sdmi centric) --- example/01-logical-replication/sysconfig.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 example/01-logical-replication/sysconfig.json diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json new file mode 100644 index 0000000..ee51103 --- /dev/null +++ b/example/01-logical-replication/sysconfig.json @@ -0,0 +1,70 @@ +{ + "system": { + "config": { + "global": { + "managers": { + "failover_mode": false, + "run_on_host": true + } + }, + "roles": { + "SYSOwner": { + "id": "admin", + "password": "admin" + }, + "SYSPgDBReplication": { + "id": "replicator", + "password": "secure" + } + } + }, + "networks": [ + { + "dbpool-net": { + "config": { + "net": { + "ipv4": { + "sbunet": "192.168.10.0", + "netmask": "255.255.255.0", + "netbits": 24, + "gateway": "192.168.10.254" + }, + "parts": { + "local-1": { + "ipv4": { + "start": 1, + "end": 252, + "managers": [ 253 ] + } + } + } + }, + "permissions": { + "SYSOwner": "admin" + }, + "scale": { + "min-nodes": 2, + "max-nodes": 4, + "metrics": { + "characteristics": "default" + } + } + } + } + } + ], + "runtime": { + "network": { + "nodes": [ + { + "dbpool-net-0": { + "status": "active", + "metrics": { + } + } + } + ] + } + } + } +} From dcc4b346248859f459187551f6b1ebb997b00c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 23 Jan 2026 09:21:08 +0100 Subject: [PATCH 02/35] Single DB-node docker file --- example/01-logical-replication/db-node.dockerfile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 example/01-logical-replication/db-node.dockerfile diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile new file mode 100644 index 0000000..81cab8c --- /dev/null +++ b/example/01-logical-replication/db-node.dockerfile @@ -0,0 +1,15 @@ +FROM postgres:18-bookworm +MAINTAINER Claus Prüfer + +RUN apt-get -qq update -y +RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psycopg2 -y + +RUN pip3 install microesb --break-system-packages +RUN pip3 install jsocket --break-system-packages + +ENV POSTGRES_USER postgres +ENV POSTGRES_PASSWORD password +ENV POSTGRES_DB lb-test + +EXPOSE 5432 +EXPOSE 64000 From 337a47834d867c4810dd4ad7ed432c60fc3546fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:07:06 +0100 Subject: [PATCH 03/35] Add run container shell script --- example/01-logical-replication/run-container.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 example/01-logical-replication/run-container.sh diff --git a/example/01-logical-replication/run-container.sh b/example/01-logical-replication/run-container.sh new file mode 100755 index 0000000..fcb19c4 --- /dev/null +++ b/example/01-logical-replication/run-container.sh @@ -0,0 +1,5 @@ +#!/bin/sh +node_id=$1 +node_ip=$2 + +docker run --rm -d --name ${node_id} --net dbpool-net --ip ${node_ip} db-node From d9b0ce0704318826eb8fe6cc19971e643272aadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:09:41 +0100 Subject: [PATCH 04/35] Disable iptables in docker daemon config --- example/01-logical-replication/docker-daemon/daemon.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 example/01-logical-replication/docker-daemon/daemon.json diff --git a/example/01-logical-replication/docker-daemon/daemon.json b/example/01-logical-replication/docker-daemon/daemon.json new file mode 100644 index 0000000..7e0c4a1 --- /dev/null +++ b/example/01-logical-replication/docker-daemon/daemon.json @@ -0,0 +1,4 @@ +{ + "iptables": false +} + From 8503e668d4ebde4eb0371957d546bad4a92d321f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:10:26 +0100 Subject: [PATCH 05/35] Remove unrelevant runtime sub-data --- example/01-logical-replication/sysconfig.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index ee51103..46167a6 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -56,13 +56,6 @@ "runtime": { "network": { "nodes": [ - { - "dbpool-net-0": { - "status": "active", - "metrics": { - } - } - } ] } } From 56926999a3ddfa9a3cc089ff74d6e5a2d43f2db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:10:58 +0100 Subject: [PATCH 06/35] Add local orchestrator code --- .../01-logical-replication/orchestrator.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 example/01-logical-replication/orchestrator.py diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py new file mode 100644 index 0000000..4e4be62 --- /dev/null +++ b/example/01-logical-replication/orchestrator.py @@ -0,0 +1,30 @@ +import json +import jsocket + +def mm_connect(dst_address, dst_port=64000): + client = jsocket.JsonClient(address=dst_address, port=dst_port) + assert client.connect() is True + return client + +def mm_send(client_ref, payload): + client_ref.send_obj(payload) + return client_ref.read_obj() + +def mm_close(client_ref): + client_ref.close() + +sysconfig = None + +with open('sysconfig.json', 'r') as fh: + sysconfig = json.loads(fh.read()) + + +sysconfig + +{ + "dbpool-net-0": { + "status": "active", + "metrics": { + } + } +} From 054b10833a51e703e8dbf5e6092d62dfe68df6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:11:33 +0100 Subject: [PATCH 07/35] Add docker metadata --- example/01-logical-replication/docker-build.sh | 5 +++++ example/01-logical-replication/docker-network.sh | 3 +++ 2 files changed, 8 insertions(+) create mode 100755 example/01-logical-replication/docker-build.sh create mode 100755 example/01-logical-replication/docker-network.sh diff --git a/example/01-logical-replication/docker-build.sh b/example/01-logical-replication/docker-build.sh new file mode 100755 index 0000000..80819f3 --- /dev/null +++ b/example/01-logical-replication/docker-build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# build docker database node +docker build -t db-node --file ./db-node.dockerfile . + diff --git a/example/01-logical-replication/docker-network.sh b/example/01-logical-replication/docker-network.sh new file mode 100755 index 0000000..1c98f93 --- /dev/null +++ b/example/01-logical-replication/docker-network.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker network create --subnet=172.16.1.0/24 --gateway=172.16.1.254 -o com.docker.network.bridge.enable_ip_masquerade=false -o com.docker.network.bridge.name=dbr0 dbpool-net From 70a08d6d438b676cdfdafec13510443c7347c1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:03:20 +0100 Subject: [PATCH 08/35] Update docker related --- example/01-logical-replication/db-node.dockerfile | 4 ++++ example/01-logical-replication/docker-build.sh | 1 - example/01-logical-replication/run-container.sh | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile index 81cab8c..449ee54 100644 --- a/example/01-logical-replication/db-node.dockerfile +++ b/example/01-logical-replication/db-node.dockerfile @@ -7,6 +7,10 @@ RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psyc RUN pip3 install microesb --break-system-packages RUN pip3 install jsocket --break-system-packages +RUN mkdir /json-rpc-server +COPY ./db-node-rpc/*.py /json-rpc-server/ +COPY ./db-node-rpc/*.sh /json-rpc-server/ + ENV POSTGRES_USER postgres ENV POSTGRES_PASSWORD password ENV POSTGRES_DB lb-test diff --git a/example/01-logical-replication/docker-build.sh b/example/01-logical-replication/docker-build.sh index 80819f3..6ce5713 100755 --- a/example/01-logical-replication/docker-build.sh +++ b/example/01-logical-replication/docker-build.sh @@ -2,4 +2,3 @@ # build docker database node docker build -t db-node --file ./db-node.dockerfile . - diff --git a/example/01-logical-replication/run-container.sh b/example/01-logical-replication/run-container.sh index fcb19c4..d380b94 100755 --- a/example/01-logical-replication/run-container.sh +++ b/example/01-logical-replication/run-container.sh @@ -1,5 +1,6 @@ #!/bin/sh node_id=$1 node_ip=$2 +node_net=$3 -docker run --rm -d --name ${node_id} --net dbpool-net --ip ${node_ip} db-node +docker run --rm -d --name ${node_id} --ip ${node_ip} --net ${node_net} db-node From 1f0da9c7b13c80c31ebcdc89a7595d9bc6d13cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:03:44 +0100 Subject: [PATCH 09/35] Add service metadata templates --- .../svc_call_metadata.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 example/01-logical-replication/svc_call_metadata.py diff --git a/example/01-logical-replication/svc_call_metadata.py b/example/01-logical-replication/svc_call_metadata.py new file mode 100644 index 0000000..572b39b --- /dev/null +++ b/example/01-logical-replication/svc_call_metadata.py @@ -0,0 +1,60 @@ +update_net_topology = { + 'SYSServiceID': 'UpdateNetworkTopology', + 'data': [ + { + 'SYSBackendMethod': { 'NetworkTopology': 'update' }, + 'System': { + 'id': 'db-loadbalancing-test', + 'NetworkTopology': { + 'NetIPv4': {}, + 'HostNode': [] + } + } + } + ] +} + +set_global_db_properties = { + 'SYSServiceID': 'InitDatabase', + 'data': [ + { + 'SYSBackendMethod': { 'Database': 'init_db' }, + 'Database': { + 'id': 'lb-test' + } + } + ] +} + +create_repl_table = { + 'SYSServiceID': 'CreateReplicaTable', + 'data': [ + { + 'SYSBackendMethod': { 'Database': 'create_replica_table' }, + 'Database': { + 'id': 'lb-test', + 'Table': { + 'name': 'table1', + 'add_timestamp_cols': True, + 'attach_replication_trigger': True, + 'Column': [ + { + 'name': 'id', + 'type': 'serial', + 'primary_key': True + }, + { + 'name': 'col1', + 'type': 'varchar', + 'default': 'default-value' + }, + { + 'name': 'col2', + 'type': 'varchar' + } + ] + } + } + } + ] +} From bc2cacd5e6eafde545907ceee98573d1d3ecfa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:07 +0100 Subject: [PATCH 10/35] Add main orchestrator --- .../01-logical-replication/orchestrator.py | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index 4e4be62..d9f3e0d 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -1,5 +1,10 @@ import json +import ipcalc import jsocket +import subprocess + +import svc_call_metadata + def mm_connect(dst_address, dst_port=64000): client = jsocket.JsonClient(address=dst_address, port=dst_port) @@ -13,18 +18,49 @@ def mm_send(client_ref, payload): def mm_close(client_ref): client_ref.close() + sysconfig = None with open('sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) +network = sysconfig['system']['networks'][0] + +network_id = network['id'] +network_config = network['config'] + +network_segment = '{}/{}'.format( + network_config['net']['ipv4']['subnet'], + network_config['net']['ipv4']['netbits'] +) + +network_ipv4_addresses = iter(ipcalc.Network(network_segment)) -sysconfig +svc_net_topology = svc_call_metadata.update_net_topology['data'][0]['System']['NetworkTopology'] +svc_net_topology['NetIPv4'] = network_config['net']['ipv4'] -{ - "dbpool-net-0": { - "status": "active", - "metrics": { - } +# start containers +for i in range(0, 3): + + node_id = 'node-'+str(i) + node_ip = next(network_ipv4_addresses) + + node_cfg = { + 'name': node_id, + 'ipv4': node_ip } -} + + svc_net_topology['HostNode'].append(node_cfg) + + cmd_run_container = [] + cmd_run_container.append('./run-container.sh') + cmd_run_container.append(node_id) + cmd_run_container.append(str(node_ip)) + cmd_run_container.append(network['id']) + + subprocess.run(cmd_run_container, capture_output=True) + + cmd_start_server = 'docker exec {} /json-rpc-server/start-server.sh'.format(node_id) + res = subprocess.run(cmd_start_server, shell=True, capture_output=True) + test = res.stdout.strip() + print(test) From 34c7f629ceb4dc9d4afc821e21a3f6bb8d08ceee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:28 +0100 Subject: [PATCH 11/35] Update config --- example/01-logical-replication/sysconfig.json | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index 46167a6..fb072b1 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -1,11 +1,10 @@ { "system": { "config": { - "global": { - "managers": { - "failover_mode": false, - "run_on_host": true - } + "network": { + "failover_mode": false, + "run_on_host": true, + "managers": [ "172.16.1.254" ] }, "roles": { "SYSOwner": { @@ -14,40 +13,40 @@ }, "SYSPgDBReplication": { "id": "replicator", - "password": "secure" + "password": "replicator" } } }, "networks": [ { - "dbpool-net": { - "config": { - "net": { - "ipv4": { - "sbunet": "192.168.10.0", - "netmask": "255.255.255.0", - "netbits": 24, - "gateway": "192.168.10.254" - }, - "parts": { - "local-1": { - "ipv4": { - "start": 1, - "end": 252, - "managers": [ 253 ] - } - } - } - }, - "permissions": { - "SYSOwner": "admin" + "id": "dbpool-net", + "config": { + "net": { + "ipv4": { + "subnet": "172.16.1.0", + "netmask": "255.255.255.0", + "netbits": 24, + "gateway": "172.16.1.254", + "hostaddress": "172.16.1.254" }, - "scale": { - "min-nodes": 2, - "max-nodes": 4, - "metrics": { - "characteristics": "default" + "parts": [ + { + "id": "dbpool-net-1", + "ipv4": { + "start": 1, + "end": 253 + } } + ] + }, + "permissions": { + "SYSOwner": "admin" + }, + "scale": { + "min-nodes": 2, + "max-nodes": 4, + "metrics": { + "characteristics": "default" } } } From 75b90c43fbd3f64cd345c3b6d17e6c6ed051cd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:44 +0100 Subject: [PATCH 12/35] Add json-rpc server component --- .../db-node-rpc/json-rpc-server.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 example/01-logical-replication/db-node-rpc/json-rpc-server.py diff --git a/example/01-logical-replication/db-node-rpc/json-rpc-server.py b/example/01-logical-replication/db-node-rpc/json-rpc-server.py new file mode 100644 index 0000000..4ded7d4 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/json-rpc-server.py @@ -0,0 +1,52 @@ +import sys +import jsocket +import logging +import subprocess + +from microesb import microesb + +from class_reference import references as class_reference +from service_properties import service_properties +from class_mapping import class_mapping + +logging.getLogger().addHandler( + logging.StreamHandler(sys.stdout) +) + +logging.getLogger().setLevel( + logging.DEBUG +) + + +def get_current_ip_address(): + cmd_get_ip = 'ip -h addr show dev eth0 | grep inet | cut -d " " -f 6' + res = subprocess.run(cmd_get_ip, shell=True, capture_output=True) + raw_ip = res.stdout.strip() + raw_ip_sep = raw_ip.find(b'/') + return raw_ip[:raw_ip_sep] + + +class JSONServer(jsocket.ThreadedServer): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def _process_message(self, call_obj): + if isinstance(call_obj, dict): + class_mapper = microesb.ClassMapper( + class_references=class_reference[call_object['SYSServiceID']], + class_mappings=class_mapping, + class_properties=service_properties + ) + return microesb.ServiceExecuter().execute_get_hierarchy( + class_mapper=class_mapper, + service_data=call_obj + ) + return None + + +server = JSONServer( + address=get_current_ip_address(), + port=64000) + +server.start() From 3676b5f7957a0ec2d728f864d497bf7548b7909d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:05:18 +0100 Subject: [PATCH 13/35] Add server start script --- example/01-logical-replication/db-node-rpc/start-server.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 example/01-logical-replication/db-node-rpc/start-server.sh diff --git a/example/01-logical-replication/db-node-rpc/start-server.sh b/example/01-logical-replication/db-node-rpc/start-server.sh new file mode 100755 index 0000000..3ebf1fa --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/start-server.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd /json-rpc-server && python3 ./json-rpc-server.py & From 6af53deb6d59327b7491c1c7a6c3eb48b54a7dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:05:41 +0100 Subject: [PATCH 14/35] Add ESB metadata / processing logic --- .../db-node-rpc/class_mapping.py | 10 ++ .../db-node-rpc/class_reference.py | 43 ++++++ .../db-node-rpc/esbconfig.py | 12 ++ .../db-node-rpc/service_implementation.py | 68 +++++++++ .../db-node-rpc/service_properties.py | 141 ++++++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 example/01-logical-replication/db-node-rpc/class_mapping.py create mode 100644 example/01-logical-replication/db-node-rpc/class_reference.py create mode 100644 example/01-logical-replication/db-node-rpc/esbconfig.py create mode 100644 example/01-logical-replication/db-node-rpc/service_implementation.py create mode 100644 example/01-logical-replication/db-node-rpc/service_properties.py diff --git a/example/01-logical-replication/db-node-rpc/class_mapping.py b/example/01-logical-replication/db-node-rpc/class_mapping.py new file mode 100644 index 0000000..0744bb2 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/class_mapping.py @@ -0,0 +1,10 @@ +class_mapping = { + 'System': 'System', + 'NetworkTopology': 'NetworkTopology', + 'NetIPv4': 'NetIPv4', + 'NetIPv6': 'NetIPv6', + 'HostNode': 'HostNode', + 'Database': 'Database', + 'Table': 'Table', + 'Column': 'Column' +} diff --git a/example/01-logical-replication/db-node-rpc/class_reference.py b/example/01-logical-replication/db-node-rpc/class_reference.py new file mode 100644 index 0000000..04c1fb5 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/class_reference.py @@ -0,0 +1,43 @@ +references = { + 'UpdateNetworkTopology': { + 'System': { + 'property_ref': 'System', + 'children': { + 'NetworkTopology': { + 'property_ref': 'NetworkTopology', + 'children': { + 'NetIPv4': { + 'property_ref': 'NetIPv4' + }, + 'NetIPv6': { + 'property_ref': 'NetIPv6' + }, + 'HostNode': { + 'property_ref': 'HostNode' + } + } + } + } + } + }, + 'InitDatabase': { + 'Database': { + 'property_ref': 'Database' + } + }, + 'CreateReplicaTable': { + 'Database': { + 'property_ref': 'Database', + 'children': { + 'Table': { + 'property_ref': 'Table', + 'children': { + 'Column': { + 'property_ref': 'Column' + } + } + } + } + } + } +} diff --git a/example/01-logical-replication/db-node-rpc/esbconfig.py b/example/01-logical-replication/db-node-rpc/esbconfig.py new file mode 100644 index 0000000..664216e --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/esbconfig.py @@ -0,0 +1,12 @@ +import_classes = { + 'service_implementation': [ + 'System', + 'NetworkTopology', + 'NetIPv4', + 'NetIPv6', + 'HostNode', + 'Database', + 'Table', + 'Column' + ] +} diff --git a/example/01-logical-replication/db-node-rpc/service_implementation.py b/example/01-logical-replication/db-node-rpc/service_implementation.py new file mode 100644 index 0000000..96aa36a --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/service_implementation.py @@ -0,0 +1,68 @@ +import abc +import logging +import datetime + +from microesb import microesb + +logger = logging.getLogger(__name__) + + +class System(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class NetworkTopology(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + def update(self): + pass + + +class NetIPv4(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class NetIPv6(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class HostNode(microesb.MultiClassHandler): + + def __init__(self): + super().__init__() + + +class Database(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + def init_db(self): + pass + + def create_replica_table(self): + pass + + +class Table(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class Column(microesb.MultiClassHandler): + + def __init__(self): + super().__init__() + self.primary_key = False + self.name = None + self.type = None + self.default = None diff --git a/example/01-logical-replication/db-node-rpc/service_properties.py b/example/01-logical-replication/db-node-rpc/service_properties.py new file mode 100644 index 0000000..b7fe20a --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/service_properties.py @@ -0,0 +1,141 @@ +service_properties = { + 'System': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'System id' + } + } + }, + 'NetworkTopology': { + 'properties': { + 'type': { + 'type': 'str', + 'default': 'un-partitioned', + 'required': False, + 'description': 'Network topology type' + } + }, + 'methods': [ + 'update' + ] + }, + 'NetIPv4': { + 'properties': { + 'subnet': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 subnet' + }, + 'netmask': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 netmask' + }, + 'netbits': { + 'type': 'int', + 'required': True, + 'description': 'IPv4 netmask bits' + }, + 'gateway': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 gateway address' + }, + 'hostaddress': { + 'type': 'str', + 'required': False, + 'description': 'IPv4 docker host address' + } + } + }, + 'NetIPv6': { + 'properties': { + } + }, + 'HostNode': { + 'properties': { + 'name': { + 'type': 'str', + 'required': True, + 'description': 'Hostname' + }, + 'ipv4': { + 'type': 'str', + 'required': False, + 'description': 'IPv4 address' + }, + 'ipv6': { + 'type': 'str', + 'required': False, + 'description': 'IPv6 address' + } + } + }, + 'Database': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Database id' + } + }, + 'methods': [ + 'init_db', + 'create_replica_table' + ] + }, + 'Table': { + 'properties': { + 'name': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Table name' + }, + 'add_timestamp_cols': { + 'type': 'bool', + 'default': True, + 'required': False, + 'description': 'Automatically add timestamp columns for insert and update' + }, + 'attach_replication_trigger': { + 'type': 'bool', + 'default': True, + 'required': False, + 'description': 'Automatically attach replication check trigger (currently only update)' + }, + } + }, + 'Column': { + 'properties': { + 'primary_key': { + 'type': 'bool', + 'default': False, + 'required': False, + 'description': 'Column primary key flag' + }, + 'name': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Column id' + }, + 'type': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Column type' + }, + 'default': { + 'type': 'str', + 'default': None, + 'required': False, + 'description': 'Column default value' + } + } + } +} From d5d8c059b3ed3fd60f22a59477f8409ce85077e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:55:54 +0100 Subject: [PATCH 15/35] Replace jsocket with local cleaned-up (working) version --- example/01-logical-replication/db-node.dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile index 449ee54..565c696 100644 --- a/example/01-logical-replication/db-node.dockerfile +++ b/example/01-logical-replication/db-node.dockerfile @@ -4,8 +4,10 @@ MAINTAINER Claus Prüfer RUN apt-get -qq update -y RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psycopg2 -y +COPY ./packages/jsocket-1.9.5.tar.gz / + RUN pip3 install microesb --break-system-packages -RUN pip3 install jsocket --break-system-packages +RUN pip3 install ./jsocket-1.9.5.tar.gz --break-system-packages RUN mkdir /json-rpc-server COPY ./db-node-rpc/*.py /json-rpc-server/ From ee47dcd933012d9ab84be25e8278e9e5c5ece99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:56:39 +0100 Subject: [PATCH 16/35] Fix bugs --- example/01-logical-replication/orchestrator.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index d9f3e0d..a6b34b1 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -18,12 +18,11 @@ def mm_send(client_ref, payload): def mm_close(client_ref): client_ref.close() - -sysconfig = None - +# load configuration with open('sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) +# model config parts network = sysconfig['system']['networks'][0] network_id = network['id'] @@ -34,6 +33,7 @@ def mm_close(client_ref): network_config['net']['ipv4']['netbits'] ) +# make network segment iterator network_ipv4_addresses = iter(ipcalc.Network(network_segment)) svc_net_topology = svc_call_metadata.update_net_topology['data'][0]['System']['NetworkTopology'] @@ -47,7 +47,7 @@ def mm_close(client_ref): node_cfg = { 'name': node_id, - 'ipv4': node_ip + 'ipv4': str(node_ip) } svc_net_topology['HostNode'].append(node_cfg) @@ -61,6 +61,10 @@ def mm_close(client_ref): subprocess.run(cmd_run_container, capture_output=True) cmd_start_server = 'docker exec {} /json-rpc-server/start-server.sh'.format(node_id) - res = subprocess.run(cmd_start_server, shell=True, capture_output=True) - test = res.stdout.strip() - print(test) + res = subprocess.run(cmd_start_server, shell=True, capture_output=True, check=True) + +for node in svc_net_topology['HostNode']: + + client = mm_connect(node['ipv4']) + res = mm_send(client, svc_call_metadata.update_net_topology, check=True) + print(res) From 74a442e604bd9813f58fa7b7f8e24edf6e21a38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:57:04 +0100 Subject: [PATCH 17/35] Add missing default values --- example/01-logical-replication/svc_call_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/01-logical-replication/svc_call_metadata.py b/example/01-logical-replication/svc_call_metadata.py index 572b39b..5af979f 100644 --- a/example/01-logical-replication/svc_call_metadata.py +++ b/example/01-logical-replication/svc_call_metadata.py @@ -2,7 +2,7 @@ 'SYSServiceID': 'UpdateNetworkTopology', 'data': [ { - 'SYSBackendMethod': { 'NetworkTopology': 'update' }, + 'SYSBackendMethod': { 'System': 'update_network_topology' }, 'System': { 'id': 'db-loadbalancing-test', 'NetworkTopology': { From acdcb348ed63913fd81b7432c6cf8b61a77d12c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:57:36 +0100 Subject: [PATCH 18/35] Update (bug fix, working with modified jsocket) --- .../db-node-rpc/json-rpc-server.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/json-rpc-server.py b/example/01-logical-replication/db-node-rpc/json-rpc-server.py index 4ded7d4..d7e275a 100644 --- a/example/01-logical-replication/db-node-rpc/json-rpc-server.py +++ b/example/01-logical-replication/db-node-rpc/json-rpc-server.py @@ -26,27 +26,28 @@ def get_current_ip_address(): return raw_ip[:raw_ip_sep] -class JSONServer(jsocket.ThreadedServer): +class JSONServer(jsocket.JsonServer): def __init__(self, **kwargs): super().__init__(**kwargs) def _process_message(self, call_obj): if isinstance(call_obj, dict): + print(call_obj) class_mapper = microesb.ClassMapper( - class_references=class_reference[call_object['SYSServiceID']], + class_references=class_reference[call_obj['SYSServiceID']], class_mappings=class_mapping, class_properties=service_properties ) - return microesb.ServiceExecuter().execute_get_hierarchy( + res = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, service_data=call_obj ) - return None + return res[0]['System']['object_instance'].json_dict + return { "Status": "NoObject received" } server = JSONServer( address=get_current_ip_address(), - port=64000) - -server.start() + port=64000 +).server_loop() From 81999a1c663933e1ab762ff3f2e3e3b570a0c54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:59:13 +0100 Subject: [PATCH 19/35] Add missing default values --- .../db-node-rpc/service_properties.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/service_properties.py b/example/01-logical-replication/db-node-rpc/service_properties.py index b7fe20a..07ee7cd 100644 --- a/example/01-logical-replication/db-node-rpc/service_properties.py +++ b/example/01-logical-replication/db-node-rpc/service_properties.py @@ -7,7 +7,10 @@ 'required': True, 'description': 'System id' } - } + }, + 'methods': [ + 'update_network_topology' + ] }, 'NetworkTopology': { 'properties': { @@ -17,35 +20,37 @@ 'required': False, 'description': 'Network topology type' } - }, - 'methods': [ - 'update' - ] + } }, 'NetIPv4': { 'properties': { 'subnet': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 subnet' }, 'netmask': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 netmask' }, 'netbits': { 'type': 'int', + 'default': None, 'required': True, 'description': 'IPv4 netmask bits' }, 'gateway': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 gateway address' }, 'hostaddress': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv4 docker host address' } @@ -59,16 +64,19 @@ 'properties': { 'name': { 'type': 'str', + 'default': None, 'required': True, 'description': 'Hostname' }, 'ipv4': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv4 address' }, 'ipv6': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv6 address' } From f3c316ac2979a8884176b014555af3d8cad2a034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:59:44 +0100 Subject: [PATCH 20/35] Disable iptables globally in docker-daemon --- example/01-logical-replication/docker-daemon/daemon.json | 1 - 1 file changed, 1 deletion(-) diff --git a/example/01-logical-replication/docker-daemon/daemon.json b/example/01-logical-replication/docker-daemon/daemon.json index 7e0c4a1..326fe13 100644 --- a/example/01-logical-replication/docker-daemon/daemon.json +++ b/example/01-logical-replication/docker-daemon/daemon.json @@ -1,4 +1,3 @@ { "iptables": false } - From adbe82e1f7c8bd2aa1246b43e4999245a0adf973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 31 Jan 2026 08:33:46 +0100 Subject: [PATCH 21/35] Update RPC model --- .../db-node-rpc/class_mapping.py | 3 +- .../db-node-rpc/class_reference.py | 7 +++-- .../db-node-rpc/esbconfig.py | 3 +- .../db-node-rpc/service_implementation.py | 23 +++++++++++--- .../db-node-rpc/service_properties.py | 30 ++++++++++++++++++- 5 files changed, 57 insertions(+), 9 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/class_mapping.py b/example/01-logical-replication/db-node-rpc/class_mapping.py index 0744bb2..340ffc1 100644 --- a/example/01-logical-replication/db-node-rpc/class_mapping.py +++ b/example/01-logical-replication/db-node-rpc/class_mapping.py @@ -1,9 +1,10 @@ class_mapping = { 'System': 'System', + 'Network': 'Network', 'NetworkTopology': 'NetworkTopology', 'NetIPv4': 'NetIPv4', 'NetIPv6': 'NetIPv6', - 'HostNode': 'HostNode', + 'TopologyHost': 'TopologyHost', 'Database': 'Database', 'Table': 'Table', 'Column': 'Column' diff --git a/example/01-logical-replication/db-node-rpc/class_reference.py b/example/01-logical-replication/db-node-rpc/class_reference.py index 04c1fb5..a0ad0da 100644 --- a/example/01-logical-replication/db-node-rpc/class_reference.py +++ b/example/01-logical-replication/db-node-rpc/class_reference.py @@ -3,6 +3,9 @@ 'System': { 'property_ref': 'System', 'children': { + 'Network': { + 'property_ref': 'Network' + }, 'NetworkTopology': { 'property_ref': 'NetworkTopology', 'children': { @@ -12,8 +15,8 @@ 'NetIPv6': { 'property_ref': 'NetIPv6' }, - 'HostNode': { - 'property_ref': 'HostNode' + 'TopologyHost': { + 'property_ref': 'TopologyHost' } } } diff --git a/example/01-logical-replication/db-node-rpc/esbconfig.py b/example/01-logical-replication/db-node-rpc/esbconfig.py index 664216e..b10ca8d 100644 --- a/example/01-logical-replication/db-node-rpc/esbconfig.py +++ b/example/01-logical-replication/db-node-rpc/esbconfig.py @@ -1,10 +1,11 @@ import_classes = { 'service_implementation': [ 'System', + 'Network', 'NetworkTopology', + 'TopologyHost', 'NetIPv4', 'NetIPv6', - 'HostNode', 'Database', 'Table', 'Column' diff --git a/example/01-logical-replication/db-node-rpc/service_implementation.py b/example/01-logical-replication/db-node-rpc/service_implementation.py index 96aa36a..0e06f79 100644 --- a/example/01-logical-replication/db-node-rpc/service_implementation.py +++ b/example/01-logical-replication/db-node-rpc/service_implementation.py @@ -1,4 +1,5 @@ import abc +import json import logging import datetime @@ -12,14 +13,28 @@ class System(microesb.ClassHandler): def __init__(self): super().__init__() + def update_network_topology(self): -class NetworkTopology(microesb.ClassHandler): + self.json_transform() + + net_config = {} + net_config['Network'] = self.Network.json_dict + net_config['NetworkTopology'] = self.NetworkTopology.TopologyHost.json_dict + + with open('/tmp/net-config.json', 'w')as fh: + fh.write(json.dumps(net_config)) + + +class Network(microesb.ClassHandler): def __init__(self): super().__init__() - def update(self): - pass + +class NetworkTopology(microesb.ClassHandler): + + def __init__(self): + super().__init__() class NetIPv4(microesb.ClassHandler): @@ -34,7 +49,7 @@ def __init__(self): super().__init__() -class HostNode(microesb.MultiClassHandler): +class TopologyHost(microesb.MultiClassHandler): def __init__(self): super().__init__() diff --git a/example/01-logical-replication/db-node-rpc/service_properties.py b/example/01-logical-replication/db-node-rpc/service_properties.py index 07ee7cd..683ff58 100644 --- a/example/01-logical-replication/db-node-rpc/service_properties.py +++ b/example/01-logical-replication/db-node-rpc/service_properties.py @@ -12,6 +12,34 @@ 'update_network_topology' ] }, + 'Network': { + 'properties': { + 'hostname': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Network host name' + }, + 'domain': { + 'type': 'str', + 'default': 'default.localnet', + 'required': True, + 'description': 'Network domain (name)' + }, + 'address_v4': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Network IPv4 address' + }, + 'address_v6': { + 'type': 'str', + 'default': None, + 'required': False, + 'description': 'Network IPv6 address' + } + } + }, 'NetworkTopology': { 'properties': { 'type': { @@ -60,7 +88,7 @@ 'properties': { } }, - 'HostNode': { + 'TopologyHost': { 'properties': { 'name': { 'type': 'str', From 2091db7fe65522faac1fec2efcd367042ac43983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 31 Jan 2026 08:34:46 +0100 Subject: [PATCH 22/35] Add domain properties --- example/01-logical-replication/sysconfig.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index fb072b1..19a4678 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -29,6 +29,8 @@ "gateway": "172.16.1.254", "hostaddress": "172.16.1.254" }, + "domain": ".localnet", + "domain_prepend_netid": true, "parts": [ { "id": "dbpool-net-1", From 938d19a4a610c089f687df7a0c182072d4ad8d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 31 Jan 2026 08:37:34 +0100 Subject: [PATCH 23/35] Update service call metadata --- example/01-logical-replication/svc_call_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/01-logical-replication/svc_call_metadata.py b/example/01-logical-replication/svc_call_metadata.py index 5af979f..742e023 100644 --- a/example/01-logical-replication/svc_call_metadata.py +++ b/example/01-logical-replication/svc_call_metadata.py @@ -5,9 +5,10 @@ 'SYSBackendMethod': { 'System': 'update_network_topology' }, 'System': { 'id': 'db-loadbalancing-test', + 'Network': {}, 'NetworkTopology': { 'NetIPv4': {}, - 'HostNode': [] + 'TopologyHost': [] } } } From c2ff7705a8da70a14e86bed6a7d142103a2115af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 31 Jan 2026 08:37:55 +0100 Subject: [PATCH 24/35] Update --- .../db-node-rpc/json-rpc-server.py | 8 +++---- .../01-logical-replication/orchestrator.py | 21 ++++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/json-rpc-server.py b/example/01-logical-replication/db-node-rpc/json-rpc-server.py index d7e275a..1e3483d 100644 --- a/example/01-logical-replication/db-node-rpc/json-rpc-server.py +++ b/example/01-logical-replication/db-node-rpc/json-rpc-server.py @@ -33,18 +33,18 @@ def __init__(self, **kwargs): def _process_message(self, call_obj): if isinstance(call_obj, dict): - print(call_obj) + print('Call obj:{}'.format(call_obj)) class_mapper = microesb.ClassMapper( class_references=class_reference[call_obj['SYSServiceID']], class_mappings=class_mapping, class_properties=service_properties ) - res = microesb.ServiceExecuter().execute_get_hierarchy( + res = microesb.ServiceExecuter().execute( class_mapper=class_mapper, service_data=call_obj ) - return res[0]['System']['object_instance'].json_dict - return { "Status": "NoObject received" } + return { "Status": "ok" } + return { "Status": "error - objtype not dict()" } server = JSONServer( diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index a6b34b1..42ba103 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -18,6 +18,7 @@ def mm_send(client_ref, payload): def mm_close(client_ref): client_ref.close() + # load configuration with open('sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) @@ -36,7 +37,10 @@ def mm_close(client_ref): # make network segment iterator network_ipv4_addresses = iter(ipcalc.Network(network_segment)) -svc_net_topology = svc_call_metadata.update_net_topology['data'][0]['System']['NetworkTopology'] +svc_system = svc_call_metadata.update_net_topology['data'][0]['System'] + +svc_net = svc_system['Network'] +svc_net_topology = svc_system['NetworkTopology'] svc_net_topology['NetIPv4'] = network_config['net']['ipv4'] # start containers @@ -44,13 +48,18 @@ def mm_close(client_ref): node_id = 'node-'+str(i) node_ip = next(network_ipv4_addresses) + #node_ip = '192.168.10.120' node_cfg = { 'name': node_id, 'ipv4': str(node_ip) } - svc_net_topology['HostNode'].append(node_cfg) + svc_net['hostname'] = node_id + svc_net['domain'] = network_config['net']['domain'] + svc_net['address_v4'] = str(node_ip) + + svc_net_topology['TopologyHost'].append(node_cfg) cmd_run_container = [] cmd_run_container.append('./run-container.sh') @@ -58,13 +67,15 @@ def mm_close(client_ref): cmd_run_container.append(str(node_ip)) cmd_run_container.append(network['id']) - subprocess.run(cmd_run_container, capture_output=True) + subprocess.run(cmd_run_container, capture_output=True, check=True) cmd_start_server = 'docker exec {} /json-rpc-server/start-server.sh'.format(node_id) res = subprocess.run(cmd_start_server, shell=True, capture_output=True, check=True) -for node in svc_net_topology['HostNode']: +for node in svc_net_topology['TopologyHost']: + + print(svc_call_metadata.update_net_topology) client = mm_connect(node['ipv4']) - res = mm_send(client, svc_call_metadata.update_net_topology, check=True) + res = mm_send(client, svc_call_metadata.update_net_topology) print(res) From cefb3869bb2a7e87684b921448e6e321f0e4ee0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 2 Feb 2026 10:09:33 +0100 Subject: [PATCH 25/35] Add global sql queries --- .../db-node-rpc/sql_queries.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 example/01-logical-replication/db-node-rpc/sql_queries.py diff --git a/example/01-logical-replication/db-node-rpc/sql_queries.py b/example/01-logical-replication/db-node-rpc/sql_queries.py new file mode 100644 index 0000000..8cd7b8b --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/sql_queries.py @@ -0,0 +1,13 @@ +init_db = ''' +ALTER SYSTEM SET wal_level = logical; +ALTER SYSTEM SET max_replication_slots = 10; +ALTER SYSTEM SET max_wal_senders = 10; + +CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'replicator'; +''' + +create_table = ''' +CREATE TABLE {tablename} ( +{tablecolumns} +); +''' From 9bd2bbec9f947531e169c5a1a67cbc59ab4e3b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 2 Feb 2026 10:10:12 +0100 Subject: [PATCH 26/35] Fix path --- example/01-logical-replication/orchestrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index 42ba103..42320cf 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -20,7 +20,7 @@ def mm_close(client_ref): # load configuration -with open('sysconfig.json', 'r') as fh: +with open('./sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) # model config parts From 0af58da5fee3fbf417720aed401a0b1b38904647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 2 Feb 2026 10:10:36 +0100 Subject: [PATCH 27/35] Remove unneeded modules --- .../db-node-rpc/service_implementation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/service_implementation.py b/example/01-logical-replication/db-node-rpc/service_implementation.py index 0e06f79..48f0bd6 100644 --- a/example/01-logical-replication/db-node-rpc/service_implementation.py +++ b/example/01-logical-replication/db-node-rpc/service_implementation.py @@ -1,7 +1,5 @@ -import abc import json import logging -import datetime from microesb import microesb From f97c08d92fbad562e34a5fbb44657fb68e7d0eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 5 Feb 2026 08:28:17 +0100 Subject: [PATCH 28/35] Add queries --- .../db-node-rpc/sql_queries.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/sql_queries.py b/example/01-logical-replication/db-node-rpc/sql_queries.py index 8cd7b8b..6b17f8e 100644 --- a/example/01-logical-replication/db-node-rpc/sql_queries.py +++ b/example/01-logical-replication/db-node-rpc/sql_queries.py @@ -4,10 +4,24 @@ ALTER SYSTEM SET max_wal_senders = 10; CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'replicator'; +CREATE ROLE testreader WITH LOGIN PASSWORD 'testreader'; +CREATE ROLE testwriter WITH LOGIN PASSWORD 'testwriter'; ''' create_table = ''' -CREATE TABLE {tablename} ( -{tablecolumns} +CREATE TABLE {table_name} ( +{table_columns} ); + +GRANT SELECT ON TABLE {table_name} TO replicator; +GRANT SELECT ON TABLE {table_name} TO testreader; +GRANT INSERT ON TABLE {table_name} TO testwriter; +''' + +create_publication = ''' +CREATE PUBLICATION {publication_id} FOR TABLE {table_name}; +''' + +create_subscription = ''' +CREATE SUBSCRIPTION {subscription_id} CONNECTION 'host={host_ip} dbname=postgres port=5432' PUBLICATION {publication_id} WITH (copy_data = false, origin = none); ''' From 87b7d7ca11a9ce113f0de7427aad4cecedd7ad4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 5 Feb 2026 08:28:43 +0100 Subject: [PATCH 29/35] Increase max nodes --- example/01-logical-replication/sysconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index 19a4678..777307d 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -46,7 +46,7 @@ }, "scale": { "min-nodes": 2, - "max-nodes": 4, + "max-nodes": 6, "metrics": { "characteristics": "default" } From 70f8121ae97adda75dcaef4806c4476e10e35592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 5 Feb 2026 08:28:57 +0100 Subject: [PATCH 30/35] Make use of max_nodes config property --- example/01-logical-replication/orchestrator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index 42320cf..9dffa83 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -28,6 +28,7 @@ def mm_close(client_ref): network_id = network['id'] network_config = network['config'] +network_config_scale = network['config']['scale'] network_segment = '{}/{}'.format( network_config['net']['ipv4']['subnet'], @@ -43,8 +44,11 @@ def mm_close(client_ref): svc_net_topology = svc_system['NetworkTopology'] svc_net_topology['NetIPv4'] = network_config['net']['ipv4'] +# get node-count from config +count_nodes = network_config_scale['max-nodes'] + # start containers -for i in range(0, 3): +for i in range(0, count_nodes): node_id = 'node-'+str(i) node_ip = next(network_ipv4_addresses) From 0840382e32a3d98403ab78714950945459fd55a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 5 Feb 2026 11:28:58 +0100 Subject: [PATCH 31/35] Describe replication order --- .../01-logical-replication/REPLICATION-ORDER.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 example/01-logical-replication/REPLICATION-ORDER.md diff --git a/example/01-logical-replication/REPLICATION-ORDER.md b/example/01-logical-replication/REPLICATION-ORDER.md new file mode 100644 index 0000000..e23d183 --- /dev/null +++ b/example/01-logical-replication/REPLICATION-ORDER.md @@ -0,0 +1,17 @@ +# Precise Replication Order + +| NodeID | SrcNode | Function | ProcOn | Command | +| ------- | ------- | ----------------- | ------ | ----------------------------- | +| Node1 | node1 | node_pub_self | node | create pub 'pub-node1-table1' | +| | | | | | +| Node2 | node2 | node_pub_self | node | create pub 'pub-node2-table1' | +| | node2 | node_sub_2others | node | create sub 'sub-node1-table1' | +| | | | | | +| | node1 | others_sub_2node | orch | create sub 'sub-node2-table1' | +| | | | | | +| Node3 | node3 | node_pub_self | node | create pub 'pub-node3-table1' | +| | node3 | node_sub_2others | node | create sub 'sub-node2-table1' | +| | node3 | node_sub_2others | node | create sub 'sub-node1-table1' | +| | | | | | +| | node2 | others_sub_2node | orch | create sub 'sub-node3-table1' | +| | node1 | others_sub_2node | orch | create sub 'sub-node3-table1' | From f8bf3203c6238c7b5e5927fdea8bb47a4cdb4f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Thu, 5 Feb 2026 11:30:42 +0100 Subject: [PATCH 32/35] Remove empty lines --- example/01-logical-replication/REPLICATION-ORDER.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/example/01-logical-replication/REPLICATION-ORDER.md b/example/01-logical-replication/REPLICATION-ORDER.md index e23d183..06afa36 100644 --- a/example/01-logical-replication/REPLICATION-ORDER.md +++ b/example/01-logical-replication/REPLICATION-ORDER.md @@ -3,15 +3,11 @@ | NodeID | SrcNode | Function | ProcOn | Command | | ------- | ------- | ----------------- | ------ | ----------------------------- | | Node1 | node1 | node_pub_self | node | create pub 'pub-node1-table1' | -| | | | | | | Node2 | node2 | node_pub_self | node | create pub 'pub-node2-table1' | | | node2 | node_sub_2others | node | create sub 'sub-node1-table1' | -| | | | | | | | node1 | others_sub_2node | orch | create sub 'sub-node2-table1' | -| | | | | | | Node3 | node3 | node_pub_self | node | create pub 'pub-node3-table1' | | | node3 | node_sub_2others | node | create sub 'sub-node2-table1' | | | node3 | node_sub_2others | node | create sub 'sub-node1-table1' | -| | | | | | | | node2 | others_sub_2node | orch | create sub 'sub-node3-table1' | | | node1 | others_sub_2node | orch | create sub 'sub-node3-table1' | From 1090568146f8cd973602a49126d135787b520497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 7 Feb 2026 14:10:23 +0100 Subject: [PATCH 33/35] Correct psycopg2 syntax --- .../db-node-rpc/sql_queries.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/sql_queries.py b/example/01-logical-replication/db-node-rpc/sql_queries.py index 6b17f8e..e9d075f 100644 --- a/example/01-logical-replication/db-node-rpc/sql_queries.py +++ b/example/01-logical-replication/db-node-rpc/sql_queries.py @@ -9,19 +9,19 @@ ''' create_table = ''' -CREATE TABLE {table_name} ( -{table_columns} +CREATE TABLE %(table_name)s ( +%(table_columns)s ); -GRANT SELECT ON TABLE {table_name} TO replicator; -GRANT SELECT ON TABLE {table_name} TO testreader; -GRANT INSERT ON TABLE {table_name} TO testwriter; +GRANT SELECT ON TABLE %(table_name)s TO replicator; +GRANT SELECT ON TABLE %(table_name)s TO testreader; +GRANT INSERT ON TABLE %(table_name)s TO testwriter; ''' create_publication = ''' -CREATE PUBLICATION {publication_id} FOR TABLE {table_name}; +CREATE PUBLICATION %(publication_id)s FOR TABLE %(table_name)s; ''' create_subscription = ''' -CREATE SUBSCRIPTION {subscription_id} CONNECTION 'host={host_ip} dbname=postgres port=5432' PUBLICATION {publication_id} WITH (copy_data = false, origin = none); +CREATE SUBSCRIPTION %(subscription_id)s CONNECTION 'host=%(host_ip)s dbname=postgres port=5432' PUBLICATION %(publication_id)s WITH (copy_data = false, origin = none); ''' From f1a25736a12e3f94313e4c9818cc9b58b0f1eeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 7 Feb 2026 14:10:40 +0100 Subject: [PATCH 34/35] Add network reference property --- example/01-logical-replication/sysconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index 777307d..4e883f9 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -39,7 +39,8 @@ "end": 253 } } - ] + ], + "parent": null }, "permissions": { "SYSOwner": "admin" From 7ade6060fd5ca68334236a6a57df3430fc06e7af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 7 Feb 2026 14:12:28 +0100 Subject: [PATCH 35/35] Column replacement will be handled internally --- example/01-logical-replication/db-node-rpc/sql_queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/01-logical-replication/db-node-rpc/sql_queries.py b/example/01-logical-replication/db-node-rpc/sql_queries.py index e9d075f..779affb 100644 --- a/example/01-logical-replication/db-node-rpc/sql_queries.py +++ b/example/01-logical-replication/db-node-rpc/sql_queries.py @@ -10,7 +10,7 @@ create_table = ''' CREATE TABLE %(table_name)s ( -%(table_columns)s + {table_columns} ); GRANT SELECT ON TABLE %(table_name)s TO replicator;