Skip to content

Conversation

LucBerge
Copy link
Contributor

@LucBerge LucBerge commented Sep 25, 2024

WHY

Fix #14074

Summary by CodeRabbit

  • New Features

    • Updated pagination logic for improved data retrieval efficiency.
  • Version Updates

    • Incremented version numbers for several modules, including:
      • nocodb-add-record: 0.0.3 → 0.0.4
      • nocodb-delete-record: 0.0.3 → 0.0.4
      • nocodb-get-record: 0.0.3 → 0.0.4
      • nocodb-list-records-matching-criteria: 0.0.4 → 0.0.5
      • nocodb-update-record: 0.0.3 → 0.0.4
      • @pipedream/nocodb: 0.0.6 → 0.0.7
      • nocodb-new-record: 0.0.4 → 0.0.5
      • nocodb-updated-record: 0.0.4 → 0.0.5

Copy link

vercel bot commented Sep 25, 2024

Someone is attempting to deploy a commit to the Pipedreamers Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

vercel bot commented Sep 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Sep 27, 2024 2:41am

Copy link
Contributor

coderabbitai bot commented Sep 25, 2024

Walkthrough

The changes update several modules in the NocoDB components, primarily focusing on version increments for various actions and sources. A significant modification is made to the pagination logic in nocodb.app.mjs, where the previous page parameter is replaced with offset and limit parameters, enhancing how data is retrieved. The overall structure of the affected functions remains unchanged, but the method of tracking pagination has shifted.

Changes

File Path Change Summary
components/nocodb/nocodb.app.mjs Modified pagination logic to use offset and limit instead of page. Removed page parameter and adjusted the loop for yielding items.
components/nocodb/actions/add-record/add-record.mjs Version updated from "0.0.3" to "0.0.4".
components/nocodb/actions/delete-record/delete-record.mjs Version updated from "0.0.3" to "0.0.4".
components/nocodb/actions/get-record/get-record.mjs Version updated from "0.0.3" to "0.0.4".
components/nocodb/actions/list-records-matching-criteria/list-records-matching-criteria.mjs Version updated from "0.0.4" to "0.0.5".
components/nocodb/actions/update-record/update-record.mjs Version updated from "0.0.3" to "0.0.4".
components/nocodb/package.json Version updated from "0.0.6" to "0.0.7".
components/nocodb/sources/new-record/new-record.mjs Version updated from "0.0.4" to "0.0.5".
components/nocodb/sources/updated-record/updated-record.mjs Version updated from "0.0.4" to "0.0.5".

Assessment against linked issues

Objective Addressed Explanation
Retrieve all records from a table using pagination (14074)
Fix the pagination method to correctly handle multiple records (14074)

Possibly related PRs

Suggested labels

bug, enhancement

Suggested reviewers

  • GTFalcao

Poem

In the meadow where data flows,
A rabbit hops where pagination grows.
With offset and limit, we leap and bound,
Fetching treasures from the ground.
Hooray for changes, swift and bright,
In our code, all feels just right! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dylburger dylburger added the User submitted Submitted by a user label Sep 25, 2024
@pipedream-component-development
Copy link
Collaborator

Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified.

@pipedream-component-development
Copy link
Collaborator

Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
components/nocodb/nocodb.app.mjs (2)

125-126: Approve the change to offset-based pagination, but suggest improvements.

The switch from page-based to offset-based pagination is a good improvement that allows for more flexible data retrieval. However, there are a few points to consider:

  1. The hard-coded limit of 1000 might not be suitable for all use cases or API endpoints. Consider making this configurable, either as a parameter to the paginate method or as a class constant that can be overridden if needed.

  2. There's no check to ensure that the offset doesn't exceed the total number of items. Consider adding a check using the pageInfo object to prevent unnecessary API calls.

Consider the following improvements:

async *paginate({
  fn, args = {}, max, limit = 1000
}) {
  let lastPage, count = 0;
  args.params = {
    ...args.params,
    offset: 0,
    limit
  };
  do {
    const {
      list, pageInfo,
    } = await fn(args);
    for (const item of list) {
      yield item;
      if (max && ++count === max) {
        return;
      }
    }
    args.params.offset += list.length;
    lastPage = !pageInfo.isLastPage;
  } while (lastPage && (!pageInfo.totalItems || args.params.offset < pageInfo.totalItems));
}

This refactored version:

  1. Makes the limit configurable with a default of 1000.
  2. Increments the offset by the number of items in each page, not per item.
  3. Adds a check to stop pagination if we've reached the total number of items (assuming pageInfo.totalItems exists).

125-139: Summary of pagination changes and their impact

The changes to the paginate method address the pagination issue mentioned in the PR objectives by switching from a page-based to an offset-based approach. This change allows for more flexible data retrieval, especially for large datasets.

However, there are a few areas where the implementation can be improved:

  1. The hard-coded limit of 1000 items per page might not be suitable for all use cases.
  2. The offset increment logic needs to be adjusted to correctly paginate through the data.
  3. Additional checks can be added to prevent unnecessary API calls when all data has been retrieved.

These improvements will make the pagination more robust and efficient, ensuring that it works correctly across different scenarios and API endpoints.

Consider the following architectural improvements:

  1. Make the limit parameter configurable, allowing it to be set based on the specific requirements of each API endpoint or use case.
  2. Implement a mechanism to track the total number of items (if provided by the API) to optimize the pagination process and prevent unnecessary API calls.
  3. Add error handling to manage potential issues with the API responses, ensuring that the pagination process is resilient to unexpected data formats or server errors.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2fab769 and 7910d70.

📒 Files selected for processing (1)
  • components/nocodb/nocodb.app.mjs (1 hunks)

};
do {
const {
list, pageInfo,
} = await fn(args);
for (const item of list) {
args.params.offset++;
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 25, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the offset increment logic.

The current implementation increments the offset for each item yielded. This is incorrect for pagination purposes, as it will cause the next API call to skip items.

Replace the line:

args.params.offset++;

with:

// This line should be moved outside the for loop
args.params.offset += list.length;

This change ensures that the offset is incremented by the number of items retrieved in each API call, maintaining correct pagination.

Copy link
Contributor Author

@LucBerge LucBerge Sep 25, 2024

Choose a reason for hiding this comment

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

@coderabbitai Performing args.params.offset++ in the for loop or args.params.offset += list.length outside the loop is the exact same thing.

It is even better to do args.params.offset++ because in case the if (max && ++count === max) condition is met, offset is incremented by the correct number of rows returned.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai Performing args.params.offset++ in the for loop or args.params.offset += list.length outside the loop is the exact same thing.

It is even better to do args.params.offset++ because in case the if (max && ++count === max) condition is met, offset is incremented by the correct number of rows returned.

Copy link
Contributor

Choose a reason for hiding this comment

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

@LucBerge, you're right. Incrementing args.params.offset within the loop ensures the offset accurately reflects the number of items processed, especially when a max limit is specified. Thank you for the clarification. I'll note this for future reviews.


✏️ Learnings added
Learnt from: LucBerge
PR: PipedreamHQ/pipedream#14080
File: components/nocodb/nocodb.app.mjs:133-133
Timestamp: 2024-09-25T16:13:11.336Z
Learning: When implementing pagination with an offset, incrementing `args.params.offset` within the loop ensures correct tracking of the offset, particularly when a maximum count limit (`max`) is used.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Collaborator

@jcortes jcortes left a comment

Choose a reason for hiding this comment

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

Hi @LucBerge lgtm! Ready for QA!

@vunguyenhung
Copy link
Collaborator

vunguyenhung commented Sep 26, 2024

Hi @LucBerge, would you mind sharing the problem you're facing with the current implementation and steps to reproduce it? To test this, I need to verify that the underlining issue of the current implementation is fixed by your implementation.

On another note, I see that your pagination implementation has hard limit is 1000. Is this correct?

@LucBerge
Copy link
Contributor Author

LucBerge commented Sep 26, 2024

Hello @vunguyenhung ,

would you mind sharing the problem you're facing with the current implementation and steps to reproduce it?

See #14074

On another note, I see that your pagination implementation has hard limit is 1000. Is this correct?

You are correct it is hard coded.
Keep in mind, there is a difference between the field limit and the limit param in the request:

  • The field limit in pipedream refer to the max number of items to return in the output of the step. It is the max parameter in the paginate method.
  • The limit param in the request refer to the number of record to retrieve in a single page.

The second value can be changed or removed (default to 25 if missing). I do not know if it is better to have a dedicated field in pipedream for that. Maybe call it records per page ?

In Nocodb UI, the max value for records per page is 100. I am not even sure it correctly retrieves 1000 per page. Maybe it is limited to 100 even if is set 1000.

@vunguyenhung
Copy link
Collaborator

Okay, I understand your explaination for the limit field.

Could you share the isue you faced with the current implementation?

@LucBerge
Copy link
Contributor Author

LucBerge commented Sep 26, 2024

@vunguyenhung Are you able to reproduce the bug ?

With the limit field unset:

It performs a first paginate request and get the record 1 to 25. The while condition is never met because it is not the last page. So it retrieves the items 1 to 25 again and again and again....

AxiosError - Request failed with status code 429 "ThrottlerException: Too Many Requests"

Stack trace:
    at null.settle (/tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/node_modules/.pnpm/[email protected]/node_modules/axios/dist/node/axios.cjs:2019:12)
    at IncomingMessage.handleStreamEnd (/tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/node_modules/.pnpm/[email protected]/node_modules/axios/dist/node/axios.cjs:3135:11)
    at IncomingMessage.emit (node:events:531:35)
    at null.endReadableNT (node:internal/streams/readable:1696:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at null.callAxios (/tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/node_modules/.pnpm/@[email protected]/node_modules/@pipedream/platform/dist/axios.js:110:26)
    at Object.paginate (file:///tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/code/nocodb.app.mjs:130:13)
    at Object.processEvent (file:///tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/code/actions/list-records-matching-criteria/list-records-matching-criteria.mjs:63:24)
    at Object.run (file:///tmp/__pdg__/dist/code/e0aa835a6b6892354ab6646f82897d0108a707badb60982cefd947ee3430965b/code/actions/common/base.mjs:33:22)
    at null.executeComponent (/var/task/launch_worker.js:292:22)
    at MessagePort.messageHandler (/var/task/launch_worker.js:792:28)

With the limit field set to 30 (for example):

Lets imagine I have 42 items (from 1 to 42) and the current page limit is 25, I get the following returned records:
1, 2, 3, 4, ... , 24, 25, 1, 2, 3, 4 5
From 1 to 25, it is retrieved with the first pagination request with offset = 0. The last 5 are the second pagination request with offset = 0. With the max set to 30 it truncate the second request result.


BTW, the lastPage variable line

@LucBerge LucBerge requested a review from jcortes September 26, 2024 12:57
@vunguyenhung
Copy link
Collaborator

Merge on behalf of the user

@vunguyenhung
Copy link
Collaborator

Hi everyone, all test cases are passed! Ready for release!

Test report
https://vunguyenhung.notion.site/Fix-14074-Paginate-not-working-10cbf548bb5e81229383cb0a8f25b6a8

@vunguyenhung vunguyenhung merged commit 4e561f0 into PipedreamHQ:master Sep 27, 2024
9 of 10 checks passed
This was referenced Jul 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
User submitted Submitted by a user
Projects
Development

Successfully merging this pull request may close these issues.

[BUG] NocoDB list record
5 participants