docs/Platforms/FOSS/Docker/Meteor3/multitenancy.md
TLDR a) and b)
docker-compose.yml:
- METEOR_REACTIVITY_ORDER=oplog,polling
- DDP_TRANSPORT=uws
Multitenancy does not work, causes bug: round robin changing WeKan website at every webpage reload at same address.
For example:
When multiple Meteor 3 applications are running on the same server using
network_mode: "host" and are shared across the same MongoDB server,
the combination DDP_TRANSPORT=uws and METEOR_REACTIVITY_ORDER=changeStreams
is subject to a very rare but critical Context/Descriptor Confusion.
Here is a detailed technical explanation of what is happening behind the scenes and why it breaks a multi-tenant environment:
uws) and shared host networkinguws is an extremely high-performance WebSocket engine written in C++.
It bypasses many of the traditional networking layers of Node.js and
handles network sockets and their file handles (File Descriptors)
directly at a low level in its own Event Loop.
When containers are set to network_mode: "host", they are not
network-level isolated. They share the same Linux kernel
network stack and memory space for network traffic.
When your browser does a WebSocket handshake (Upgrade: websocket)
after an HTTP request, uws allocates a memory address for the socket.
The bug occurs when multiple containers do this at the same time
in the same host network environment,
the low-level C++ code in the uws engine can cross-identify socket identifiers from different processes
if the framework (in this case, an early beta integration of Meteor 3)
is unable to keep connection ownership (v8 isolation) completely separate.
changeStreams and data misroutingMongoDB's changeStreams relies on the application opening a long-lived,
real-time listening pipe to the database. It is heavier and more complex
than the old oplog table because it creates constantly changing,
stateful cursors between the application and the database.
When this is combined with uws WebSocket:
User A opens the boards.wekan.team page.
The browser requests a WebSocket connection, and Meteor creates a
changeStream listener on the database for User A's container.
At the same time, User B loads the kanban.buetzow.de page.
Since both containers talk locally to the same MongoDB process
and share the same host interface, the application layer's
reactivity engine (multiplexer) confuses which changeStream
data input belongs to which active uws socket address.
Result: A reactive data packet from the database (e.g. different customer 1 boards)
is pushed into the uws pipe that was locally open to the boards.wekan.team browser.
The browser gets a perfectly valid HTML body, but the Javascript layer draws the data
it received from the WebSocket.
sockjs and oplog fix the situation?sockjs is written in pure JavaScript (Node.js' native network stack).
It strictly follows Node.js' own process boundaries, and does not perform
direct C++-level optimizations beyond the kernel. Even though containers
share the host network, Node.js makes sure that each process's sockets
remain strictly within its own application window.
oplog is a more passive way to read the database. The application
reads the raw log file (local.oplog.rs) and filters its own data from
there, instead of MongoDB's active changeStream engine trying to route
and load balance cursors between different processes on the fly.
Simply put: uws and changeStreams are great tools when you are running
only one large application on a single server for which you want
maximum performance. In a multi-tenant environment
(many different clients on the same machine), they are too aggressive
and break the invisible boundaries between containers.