parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.95k stars 4.78k forks source link

LiveQueryServer crashes when configured to run with Redis cacheAdapter #9432

Open evtimmy opened 1 week ago

evtimmy commented 1 week ago

Issue Description

Running ParseLiveQueryServer where a RedisCacheAdapter is also configured causes crash when ParseLiveQueryServer tries to use the cache adapter.

Steps to reproduce

  1. Run parse-server with ParseLiveQueryServer enabled. The cache adapter I configured via config.json like so:
    "cacheAdapter": {
    "module": "../Adapters/Cache/RedisCacheAdapter.js",
    "options": {
    "url": {{ printf "redis://%s-redis-cache-app-server-master:6379" .Release.Name | quote }}
    "password": {{ $redisAppServerCachePassword | quote }}
    }
    }
  2. Subscribe a client to listen to a query
  3. Update an object that the query matches

Actual Outcome

error: RedisCacheAdapter error on get {"error":{}}
error: RedisCacheAdapter error on get {"error":{}}
An uncaught exception occurred: The client is closedStack Trace:
Error: The client is closed
at Commander._RedisClient_sendCommand (/parse-server/node_modules/@redis/client/dist/lib/client/index.js:510:31)
at Commander.commandsExecutor (/parse-server/node_modules/@redis/client/dist/lib/client/index.js:190:154)
at BaseClass.<computed> [as set] (/parse-server/node_modules/@redis/client/dist/lib/commander.js:8:29)
at RedisCacheAdapter.put (/parse-server/lib/Adapters/Cache/RedisCacheAdapter.js:85:24)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Expected Outcome

LiveQueryServer post update to client

Environment

Server

Client

Workaround:

disable the redis cache adapter.

parse-github-assistant[bot] commented 1 week ago

Thanks for opening this issue!

evtimmy commented 1 week ago

I did debug the initialization of the server and saw that ParseLiveQueryServer creates its own cacheController instance:

https://github.com/parse-community/parse-server/blob/91f9aca25bc6212ae27aac7af328ee1f19f058c8/src/LiveQuery/ParseLiveQueryServer.js#L62

but that instance connect() is never called. The regular ParseServer does call connect() on its own instance of the cache when initializing the startup promises.

This used to work in earlier versions of parse-server, before we switched to redis 4 client as redis 4 requires the async connect() to be explicitly called before using the client, whereas redis 3 automatically called connect() if the client was not connected.

One simple fix would be to add initialization await this.cacheController.connect() here:

https://github.com/parse-community/parse-server/blob/91f9aca25bc6212ae27aac7af328ee1f19f058c8/src/LiveQuery/ParseLiveQueryServer.js#L84-L94

I'm just not clear on why we check this.subscriber.isOpen and if this connect() may be called multiple times, or only on startup?