mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Share one connect and close listener between subscribers
Every EventSubscriber added its own connect and close listener to the connector, and Link caps the connector at 20 listeners per event. The web Control and App use 13 close listeners, so the 8th plugin subscriber made the browser log MaxListenersExceededWarning on every page load. Subscribers on a connector now share one listener for each event, which leaves the cap in place for everything else.
This commit is contained in:
2 files changed
+72
-7
No files matched your search
@@ -455,6 +455,36 @@ export interface SubscribableValue {
|
||||
isDeleted: boolean,
|
||||
}
|
||||
|
||||
/** Subscribers created on each connector, they share one connect and one close listener on it */
|
||||
const connectorSubscribers = new WeakMap<object, Set<EventSubscriber<any, any>>>();
|
||||
|
||||
/**
|
||||
* Pass connect and close events of a connector on to a subscriber
|
||||
* Uses one listener for all subscribers, so that plugins creating many subscribers do not go past the
|
||||
* listener limit set on the connector.
|
||||
* @param connector - connector of the link the subscriber was created on
|
||||
* @param subscriber - subscriber to pass the events to
|
||||
*/
|
||||
function addConnectorSubscriber(connector: WebSocketBaseConnector, subscriber: EventSubscriber<any, any>) {
|
||||
let subscribers = connectorSubscribers.get(connector);
|
||||
if (!subscribers) {
|
||||
const created = new Set<EventSubscriber<any, any>>();
|
||||
connectorSubscribers.set(connector, created);
|
||||
connector.on("connect", () => {
|
||||
for (const entry of created) {
|
||||
entry.handleConnectionEvent("connect");
|
||||
}
|
||||
});
|
||||
connector.on("close", () => {
|
||||
for (const entry of created) {
|
||||
entry.handleConnectionEvent("close");
|
||||
}
|
||||
});
|
||||
subscribers = created;
|
||||
}
|
||||
subscribers.add(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component for subscribing to and tracking updates of a remote resource
|
||||
* Multiple handlers can be subscribed at the same time
|
||||
@@ -483,13 +513,7 @@ export class EventSubscriber<E, S = null> {
|
||||
) {
|
||||
control.handle(Event, this._handleEvent.bind(this));
|
||||
// The events below exist only on websocket connectors, we can't early return because of test mocking
|
||||
const webSocketConnector = control.connector as WebSocketBaseConnector;
|
||||
webSocketConnector.on("connect", () => {
|
||||
this.handleConnectionEvent("connect");
|
||||
});
|
||||
webSocketConnector.on("close", () => {
|
||||
this.handleConnectionEvent("close");
|
||||
});
|
||||
addConnectorSubscriber(control.connector as WebSocketBaseConnector, this);
|
||||
// Only the web Control reports account changes
|
||||
const accountControl = control as Link & { onAccountUpdate?: (handler: () => void) => void };
|
||||
if (typeof accountControl.onAccountUpdate === "function") {
|
||||
|
||||
@@ -1021,6 +1021,47 @@ describe("lib/subscriptions", function() {
|
||||
it("should handle the provided event", function() {
|
||||
assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true);
|
||||
});
|
||||
|
||||
it("should share one connect and close listener on the connector", function() {
|
||||
const connector = mockControl.connector;
|
||||
const connectListeners = connector.listenerCount("connect");
|
||||
const closeListeners = connector.listenerCount("close");
|
||||
const subscribers = [
|
||||
new lib.EventSubscriber(StringPermissionEvent, mockControl),
|
||||
new lib.EventSubscriber(FunctionPermissionEvent, mockControl),
|
||||
];
|
||||
assert.equal(subscribers.length, 2);
|
||||
assert.equal(connector.listenerCount("connect"), connectListeners);
|
||||
assert.equal(connector.listenerCount("close"), closeListeners);
|
||||
});
|
||||
|
||||
it("should pass connect and close to every subscriber on the connector", function() {
|
||||
const subscribers = [
|
||||
eventSubscriber,
|
||||
new lib.EventSubscriber(StringPermissionEvent, mockControl),
|
||||
new lib.EventSubscriber(FunctionPermissionEvent, mockControl),
|
||||
];
|
||||
const received = subscribers.map(() => []);
|
||||
subscribers.forEach((subscriber, index) => {
|
||||
subscriber.handleConnectionEvent = event => { received[index].push(event); };
|
||||
});
|
||||
mockControl.connector.emit("close");
|
||||
mockControl.connector.emit("connect");
|
||||
assert.deepEqual(received, [["close", "connect"], ["close", "connect"], ["close", "connect"]]);
|
||||
});
|
||||
|
||||
it("should only pass events to subscribers on the same connector", function() {
|
||||
const otherControl = new MockControl(new MockConnector(
|
||||
addr({ controlId: 1 }),
|
||||
addr("controller"),
|
||||
));
|
||||
const other = new lib.EventSubscriber(RegisteredEvent, otherControl);
|
||||
const received = [];
|
||||
eventSubscriber.handleConnectionEvent = event => { received.push(["first", event]); };
|
||||
other.handleConnectionEvent = event => { received.push(["other", event]); };
|
||||
otherControl.connector.emit("close");
|
||||
assert.deepEqual(received, [["other", "close"]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("subscribe()", function() {
|
||||
|
||||
Reference in new issue
Block a user