Skip to content

LiveQueryServer crashes when configured to run with Redis cacheAdapter #9432

Description

@evtimmy

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 }}
  }
}
  1. Subscribe a client to listen to a query
  2. 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

  • Parse Server version: 7.4.0-alpha.5
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): Azure

Client

  • SDK (iOS, Android, JavaScript, PHP, Unity, etc): iOS/Android

Workaround:

disable the redis cache adapter.

Activity

  1. parse-github-assistant commented on Nov 13, 2024

    @parse-github-assistant

    Thanks for opening this issue!

  2. evtimmy commented on Nov 13, 2024

    @evtimmy
    Author

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

    this.cacheController = getCacheController(parseServerConfig);

    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:

    async connect() {
    if (this.subscriber.isOpen) {
    return;
    }
    if (typeof this.subscriber.connect === 'function') {
    await Promise.resolve(this.subscriber.connect());
    } else {
    this.subscriber.isOpen = true;
    }
    this._createSubscribers();
    }

    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?

  3. added
    type:bugImpaired feature or lacking behavior that is likely assumed
    on Nov 14, 2024
  4. Chilldev commented on Jan 26, 2025

    @Chilldev
    Contributor

    The crash is coming from:

    import { createClient } from 'redis';
    function createPublisher({ redisURL, redisOptions = {} }): any {
    redisOptions.no_ready_check = true;
    return createClient({ url: redisURL, ...redisOptions });
    }
    function createSubscriber({ redisURL, redisOptions = {} }): any {
    redisOptions.no_ready_check = true;
    return createClient({ url: redisURL, ...redisOptions });
    }

    Connections do not have an error handler.
    In my case it was happening when redis crashed unexpectdly or was unreachable.

    I am not sure if this issue is related:
    redis/node-redis#2624

    I created a patch to listen to connection errors and the server is no longer crashing.

    import { logger } from '../../logger';
    
    function createPublisher({ redisURL, redisOptions = {} }): any {
      redisOptions.no_ready_check = true;
      const client = createClient({ url: redisURL, ...redisOptions });
      client.on('error', (err) => {
        logger.error('RedisPubSub publisher client error', {
          error: err
        });
      });
      return client;
    }
    
    function createSubscriber({ redisURL, redisOptions = {} }): any {
      redisOptions.no_ready_check = true;
      const client = createClient({ url: redisURL, ...redisOptions });
      client.on('error', (err) => {
        logger.error('RedisPubSub subscriber client error', {
          error: err
        });
      });
      return client;
    }
  5. gcarpi commented on Feb 13, 2025

    @gcarpi

    Any update? I need this fix

  6. added
    bounty:$20Bounty applies for fixing this issue (Parse Bounty Program)
    on Feb 13, 2025
  7. parseplatformorg commented on Feb 24, 2025

    @parseplatformorg
    Contributor

    🎉 This change has been released in version 8.0.0-alpha.12

  8. parseplatformorg commented on Mar 12, 2025

    @parseplatformorg
    Contributor

    🎉 This change has been released in version 7.5.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bounty:$20Bounty applies for fixing this issue (Parse Bounty Program)state:released-7.x.xReleased as LTS versionstate:released-alphaReleased as alpha versiontype:bugImpaired feature or lacking behavior that is likely assumed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions