Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Apps-Engine method to read multiple messages from a room #32176

Merged
merged 37 commits into from
Jul 19, 2024

Conversation

Dnouv
Copy link
Member

@Dnouv Dnouv commented Apr 11, 2024

Proposed changes (including videos or screenshots)

Added a new method, getMessages to fetch messages from a specific room. This method accepts a roomId and an options object as parameters. The options object can contain limit, skip, and sort properties to control the query. The method uses the Messages.find function to fetch the messages from the database and converts them using a message raw converter obtained from the orch object.

Since a room can have a large number of messages, this method returns a maximum of 100 messages in a single call, to read more messages one can make another call passing the skip value. The default sort order is the descending order of timestamp (meaning the most recent messages will be returned), which can be overridden by passing the sort parameter.

Added a new converter convertMessageRaw it removes a few fields from the following (reason: comment ):

sender: {
  id: string,
  username: string,
  name?: string
},
editor: {
  id: string,
  username: string
},
room: {
  id: string
}

Additionally, this new method removes the delete since the delete operator is generally slow and can cause performance issues. It modifies the underlying object, which can lead to deoptimization in JavaScript engines.

Issue(s)

Steps to test or reproduce

To test as well as reproduce the issue and solution, try running the below in the App.

// before this PR
const roomMessage = await read
                .getRoomReader()
                .getMessages(message.room.id)

// after this PR 
const roomMessage = await read
                .getRoomReader()
                .getMessages(message.room.id, {
                    limit: 3,
                    sort: { createdAt: 'asc' },
                    skip: 1,
                });

Output:
(Before this PR):

  • Error method not implemented

(After this PR):

  • Array of messages

You can pass your own query to filter the messages you want to retrieve. The available options are:

  • limit: The number of messages to retrieve. (Default: 100 and Max: 100)
  • skip: The number of messages to skip.
  • sort: The sort order of the messages. e.g. { ts: -1 } will sort the messages by timestamp in descending order.

Further comments

Execution Stats on a room with 3M messages
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'meteor.rocketchat_message',
    indexFilterSet: false,
    parsedQuery: {
      '$and': [
        {
          rid: {
            '$eq': 'GENERAL'
          }
        },
        {
          _hidden: {
            '$not': {
              '$eq': true
            }
          }
        },
        {
          t: {
            '$not': {
              '$exists': true
            }
          }
        }
      ]
    },
    queryHash: '19FB9CF7',
    planCacheKey: 'E315B320',
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    winningPlan: {
      stage: 'LIMIT',
      limitAmount: 100,
      inputStage: {
        stage: 'FETCH',
        filter: {
          '$and': [
            {
              _hidden: {
                '$not': {
                  '$eq': true
                }
              }
            },
            {
              t: {
                '$not': {
                  '$exists': true
                }
              }
            }
          ]
        },
        inputStage: {
          stage: 'IXSCAN',
          keyPattern: {
            rid: 1,
            ts: 1,
            _updatedAt: 1
          },
          indexName: 'rid_1_ts_1__updatedAt_1',
          isMultiKey: false,
          multiKeyPaths: {
            rid: [],
            ts: [],
            _updatedAt: []
          },
          isUnique: false,
          isSparse: false,
          isPartial: false,
          indexVersion: 2,
          direction: 'forward',
          indexBounds: {
            rid: [
              '["GENERAL", "GENERAL"]'
            ],
            ts: [
              '[MinKey, MaxKey]'
            ],
            _updatedAt: [
              '[MinKey, MaxKey]'
            ]
          }
        }
      }
    },
    rejectedPlans: [
      {
        stage: 'LIMIT',
        limitAmount: 100,
        inputStage: {
          stage: 'FETCH',
          filter: {
            '$and': [
              {
                t: {
                  '$not': {
                    '$exists': true
                  }
                }
              },
              {
                _hidden: {
                  '$not': {
                    '$eq': true
                  }
                }
              }
            ]
          },
          inputStage: {
            stage: 'IXSCAN',
            keyPattern: {
              rid: 1,
              t: 1,
              'u._id': 1
            },
            indexName: 'rid_1_t_1_u._id_1',
            isMultiKey: false,
            multiKeyPaths: {
              rid: [],
              t: [],
              'u._id': []
            },
            isUnique: false,
            isSparse: false,
            isPartial: false,
            indexVersion: 2,
            direction: 'forward',
            indexBounds: {
              rid: [
                '["GENERAL", "GENERAL"]'
              ],
              t: [
                '[null, null]'
              ],
              'u._id': [
                '[MinKey, MaxKey]'
              ]
            }
          }
        }
      },
      {
        stage: 'LIMIT',
        limitAmount: 100,
        inputStage: {
          stage: 'FETCH',
          filter: {
            '$and': [
              {
                _hidden: {
                  '$not': {
                    '$eq': true
                  }
                }
              },
              {
                t: {
                  '$not': {
                    '$exists': true
                  }
                }
              }
            ]
          },
          inputStage: {
            stage: 'IXSCAN',
            keyPattern: {
              rid: 1,
              tcount: 1
            },
            indexName: 'rid_1_tcount_1',
            isMultiKey: false,
            multiKeyPaths: {
              rid: [],
              tcount: []
            },
            isUnique: false,
            isSparse: false,
            isPartial: false,
            indexVersion: 2,
            direction: 'forward',
            indexBounds: {
              rid: [
                '["GENERAL", "GENERAL"]'
              ],
              tcount: [
                '[MinKey, MaxKey]'
              ]
            }
          }
        }
      }
    ]
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 100,
    executionTimeMillis: 4,
    totalKeysExamined: 100,
    totalDocsExamined: 100,
    executionStages: {
      stage: 'LIMIT',
      nReturned: 100,
      executionTimeMillisEstimate: 0,
      works: 101,
      advanced: 100,
      needTime: 0,
      needYield: 0,
      saveState: 0,
      restoreState: 0,
      isEOF: 1,
      limitAmount: 100,
      inputStage: {
        stage: 'FETCH',
        filter: {
          '$and': [
            {
              _hidden: {
                '$not': {
                  '$eq': true
                }
              }
            },
            {
              t: {
                '$not': {
                  '$exists': true
                }
              }
            }
          ]
        },
        nReturned: 100,
        executionTimeMillisEstimate: 0,
        works: 100,
        advanced: 100,
        needTime: 0,
        needYield: 0,
        saveState: 0,
        restoreState: 0,
        isEOF: 0,
        docsExamined: 100,
        alreadyHasObj: 0,
        inputStage: {
          stage: 'IXSCAN',
          nReturned: 100,
          executionTimeMillisEstimate: 0,
          works: 100,
          advanced: 100,
          needTime: 0,
          needYield: 0,
          saveState: 0,
          restoreState: 0,
          isEOF: 0,
          keyPattern: {
            rid: 1,
            ts: 1,
            _updatedAt: 1
          },
          indexName: 'rid_1_ts_1__updatedAt_1',
          isMultiKey: false,
          multiKeyPaths: {
            rid: [],
            ts: [],
            _updatedAt: []
          },
          isUnique: false,
          isSparse: false,
          isPartial: false,
          indexVersion: 2,
          direction: 'forward',
          indexBounds: {
            rid: [
              '["GENERAL", "GENERAL"]'
            ],
            ts: [
              '[MinKey, MaxKey]'
            ],
            _updatedAt: [
              '[MinKey, MaxKey]'
            ]
          },
          keysExamined: 100,
          seeks: 1,
          dupsTested: 0,
          dupsDropped: 0
        }
      }
    }
  },
  command: {
    find: 'rocketchat_message',
    filter: {
      rid: 'GENERAL',
      _hidden: {
        '$ne': true
      },
      t: {
        '$exists': false
      }
    },
    limit: 100,
    '$db': 'meteor'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted'
  },
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1721412111, i: 1 }),

  },
  operationTime: Timestamp({ t: 1721412111, i: 1 })
}

Copy link
Contributor

dionisio-bot bot commented Apr 11, 2024

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

Copy link

changeset-bot bot commented Apr 11, 2024

🦋 Changeset detected

Latest commit: 8a0e0fa

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 32 packages
Name Type
@rocket.chat/meteor Minor
@rocket.chat/core-typings Minor
@rocket.chat/rest-typings Minor
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/fuselage-ui-kit Major
@rocket.chat/gazzodown Major
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-contexts Major
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/stream-hub-service Patch
@rocket.chat/api-client Patch
@rocket.chat/license Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/ddp-client Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/models Patch
@rocket.chat/ui-avatar Major
@rocket.chat/ui-client Major
@rocket.chat/ui-video-conf Major
@rocket.chat/web-ui-registration Major
@rocket.chat/instance-status Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

codecov bot commented Apr 11, 2024

Codecov Report

Attention: Patch coverage is 0% with 6 lines in your changes missing coverage. Please review.

Project coverage is 55.58%. Comparing base (4e8aa57) to head (8a0e0fa).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##           develop   #32176   +/-   ##
========================================
  Coverage    55.57%   55.58%           
========================================
  Files         2634     2634           
  Lines        57273    57279    +6     
  Branches     11860    11860           
========================================
+ Hits         31828    31837    +9     
+ Misses       22759    22751    -8     
- Partials      2686     2691    +5     
Flag Coverage Δ
e2e 54.33% <ø> (+0.03%) ⬆️
unit 72.23% <0.00%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

@d-gubert d-gubert changed the title feat: Implement the Apps Engine Room message read bridge feat: Apps-Engine method to read multiple messages from a room Apr 15, 2024
@d-gubert d-gubert marked this pull request as ready for review April 16, 2024 13:08
@d-gubert d-gubert requested review from a team as code owners April 16, 2024 13:08
d-gubert
d-gubert previously approved these changes Apr 16, 2024
apps/meteor/app/apps/server/bridges/rooms.ts Outdated Show resolved Hide resolved
Copy link
Member

@debdutdeb debdutdeb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also taking some questions back to the adr.

@Dnouv Dnouv requested a review from ggazzo June 6, 2024 05:39
@Dnouv
Copy link
Member Author

Dnouv commented Jun 20, 2024

Depends on: RocketChat/Rocket.Chat.Apps-engine#770

@d-gubert d-gubert added this to the 6.11 milestone Jul 19, 2024
@d-gubert d-gubert added the stat: QA assured Means it has been tested and approved by a company insider label Jul 19, 2024
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Jul 19, 2024
@Dnouv Dnouv dismissed debdutdeb’s stale review July 19, 2024 19:38

Approved on BE

@d-gubert d-gubert dismissed ggazzo’s stale review July 19, 2024 20:01

Change applied

@kodiakhq kodiakhq bot merged commit d0490d3 into develop Jul 19, 2024
48 checks passed
@kodiakhq kodiakhq bot deleted the new/room_msg_bridge branch July 19, 2024 20:43
debdutdeb pushed a commit that referenced this pull request Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants