Skip to content

Commit

Permalink
adjust server global active connection counter to consider multiplexi…
Browse files Browse the repository at this point in the history
…ng (#890)

* fix(server)): adjust global connection counter to handle multiplexing

* test: fix failings
  • Loading branch information
0xb4lamx authored Jan 19, 2025
1 parent 1e405ef commit 4defd26
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 8 deletions.
13 changes: 10 additions & 3 deletions packages/server/src/Hocuspocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,17 @@ export class Hocuspocus {
* Get the total number of active connections
*/
getConnectionsCount(): number {
return Array.from(this.documents.values()).reduce((acc, document) => {
acc += document.getConnectionsCount()
return acc
const uniqueSocketIds = new Set<string>()
const totalDirectConnections = Array.from(this.documents.values()).reduce((acc, document) => {
// Accumulate unique socket IDs
document.getConnections().forEach(({ socketId }) => {
uniqueSocketIds.add(socketId)
})
// Accumulate direct connections
return acc + document.directConnectionsCount
}, 0)
// Return the sum of unique socket IDs and direct connections
return uniqueSocketIds.size + totalDirectConnections
}

/**
Expand Down
26 changes: 25 additions & 1 deletion tests/server/getConnectionsCount.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from 'ava'
import { retryableAssertion } from '../utils/retryableAssertion.js'
import { newHocuspocus, newHocuspocusProvider } from '../utils/index.js'
import { newHocuspocus, newHocuspocusProvider, newHocuspocusProviderWebsocket } from '../utils/index.js'

test('returns 0 connections when there’s no one connected', async t => {
await new Promise(async resolve => {
Expand Down Expand Up @@ -87,3 +87,27 @@ test('adds and removes connections properly', async t => {
tt.is(server.getConnectionsCount(), 0)
})
})

test('multiplexed connections counts properly', async t => {
const server = await newHocuspocus()
const socket = newHocuspocusProviderWebsocket(server)

const providers = [
newHocuspocusProvider(server, { name: 'mux-1' }, {}, socket),
newHocuspocusProvider(server, { name: 'mux-2' }, {}, socket),
newHocuspocusProvider(server, { name: 'mux-3' }, {}, socket),
newHocuspocusProvider(server),
newHocuspocusProvider(server),

]

await retryableAssertion(t, tt => {
tt.is(server.getConnectionsCount(), 3)
})

providers.forEach(provider => { provider.disconnect(); provider.configuration.websocketProvider.disconnect() })

await retryableAssertion(t, tt => {
tt.is(server.getConnectionsCount(), 0)
})
})
2 changes: 1 addition & 1 deletion tests/server/onAuthenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ test('onAuthenticate readonly auth only affects 1 doc (when multiplexing)', asyn
tt.is(providerOK.status, WebSocketStatus.Connected)
tt.is(providerReadOnly.status, WebSocketStatus.Connected)
tt.is(server.getDocumentsCount(), 2)
tt.is(server.getConnectionsCount(), 2)
tt.is(server.getConnectionsCount(), 1)
tt.is(socket.status, WebSocketStatus.Connected)
})

Expand Down
2 changes: 1 addition & 1 deletion tests/server/onClose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ test('server closes connection only after receiving close event from all connect
})

await retryableAssertion(t, t2 => {
t2.is(server.getConnectionsCount(), 2)
t2.is(server.getConnectionsCount(), 1)
})

socket.shouldConnect = false
Expand Down
5 changes: 3 additions & 2 deletions tests/utils/newHocuspocusProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
HocuspocusProvider,
HocuspocusProviderConfiguration, HocuspocusProviderWebsocketConfiguration,
HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration,
} from '@hocuspocus/provider'
import { Hocuspocus } from '@hocuspocus/server'
import { newHocuspocusProviderWebsocket } from './newHocuspocusProviderWebsocket.js'
Expand All @@ -9,9 +9,10 @@ export const newHocuspocusProvider = (
server: Hocuspocus,
options: Partial<HocuspocusProviderConfiguration> = {},
websocketOptions: Partial<HocuspocusProviderWebsocketConfiguration> = {},
websocketProvider?: HocuspocusProviderWebsocket,
): HocuspocusProvider => {
return new HocuspocusProvider({
websocketProvider: newHocuspocusProviderWebsocket(server, websocketOptions),
websocketProvider: websocketProvider ?? newHocuspocusProviderWebsocket(server, websocketOptions),
// Just use a generic document name for all tests.
name: 'hocuspocus-test',
// There is no need to share data with other browser tabs in the testing environment.
Expand Down

0 comments on commit 4defd26

Please sign in to comment.