matrix-org / matrix-js-sdk

Matrix Client-Server SDK for JavaScript
Apache License 2.0
1.54k stars 582 forks source link

Client (or sync) wont stop, even with .stopClient() #2472

Open cestoliv opened 2 years ago

cestoliv commented 2 years ago

Hello, I am trying to use this module to send a notification at the end of a script. It works, except that the client only stops several minutes after calling the .stopClient() function. So my program is still awake during this time.

The program blocks after

stopping OutgoingRoomKeyRequestManager
stopping MatrixClient
SyncApi.stop

And finally after many minutes I get Sync no longer running: exiting and the program finally stops.

How can I force the client (and the synchronization) to stop immediately?

Here is the code i'm using:

import * as sdk from 'matrix-js-sdk'
import { logger } from 'matrix-js-sdk/lib/logger.js'
import { ClientEvent } from 'matrix-js-sdk'
import Olm from "olm/olm_legacy.js"

import { LocalStorage } from 'node-localstorage'
import { LocalStorageCryptoStore } from 'matrix-js-sdk/lib/crypto/store/localStorage-crypto-store.js'

// temp fix : https://github.com/matrix-org/matrix-js-sdk/issues/2415#issuecomment-1141246410
import request from "request"
///

global.Olm = Olm
const localStorage = new LocalStorage('./store/matrix')

export class Matrix {
    matrixClient
    connected

    constructor(url, user, token) {
        this.connected = false
        // temp fix : https://github.com/matrix-org/matrix-js-sdk/issues/2415#issuecomment-1141246410
        sdk.request(request)
        ///

        this.matrixClient = sdk.createClient({
            deviceId: "Streaks Server",
            baseUrl: url,
            accessToken: token,
            userId: user,

            sessionStore: new sdk.MemoryStore({ localStorage }),
            cryptoStore: new LocalStorageCryptoStore(localStorage)
        })
    }

    async connect() {
        if (this.connected)
            return
        //logger.setLevel(logger.levels.ERROR)
        await this.matrixClient.initCrypto()
        await this.matrixClient.startClient()
        await new Promise((resolve, _reject) => {
            this.matrixClient.once(ClientEvent.Sync, () => {
                // Send encrypted message, even if member isn't trusted
                this.matrixClient.setGlobalErrorOnUnknownDevices(false)
                this.connected = true
                resolve()
            })
        })
    }

    disconnect() {
        if (!this.connected)
            return
        this.matrixClient.stopClient()
    }

    async sendMessage(roomID, message) {
        await this.matrixClient.joinRoom(roomID)
        await this.matrixClient.uploadKeys()
        await this.matrixClient.sendTextMessage(roomID, message)
    }
}

const config = {
    matrix: {
        url: process.env['MATRIX_URL'],
        user: process.env['MATRIX_USER'],
        token: process.env['MATRIX_TOKEN'],
        roomID: process.env['MATRIX_ROOM']
    }
}

let matrix = new Matrix(config.matrix.url, config.matrix.user, config.matrix.token)
await matrix.connect()
await matrix.sendMessage(config.matrix.roomID, "Hello world!")
matrix.disconnect()

Full logs

t3chguy commented 2 years ago

This is not possible with the current request interface, blocked on https://github.com/matrix-org/matrix-js-sdk/issues/801

habdelra commented 1 year ago

I tracked this down to 2 specific timeouts: https://github.com/matrix-org/matrix-js-sdk/blob/858155e0efc910e5e094803bb71af78d42419780/src/crypto/index.ts#L2037-L2040 and https://github.com/matrix-org/matrix-js-sdk/blob/858155e0efc910e5e094803bb71af78d42419780/src/http-api/utils.ts#L26-L28

As a workaround you can use pnpm patch to patch this package. Specifically in node, timers have an unref() method that tells node that its ok to end the process if the timer is still running.

I patched both of these setTimeout like so:

setTimeout(....).unref();

and that allowed node to exit immediately after calling the stopClient() method.

thebalaa commented 1 year ago

I am writing some Jest based integration tests and ran into this issue:

Jest did not exit 5 seconds after the test run has completed.

'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.

Temporary workaround for anyone using Jest is to run it with the --forceExit flag, so:

  "scripts": {
...
    "test": "jest --forceExit",
...
}