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

Performance issues with paginate() #273

Closed
fsnlarson opened this issue Jan 27, 2022 · 2 comments
Closed

Performance issues with paginate() #273

fsnlarson opened this issue Jan 27, 2022 · 2 comments
Assignees
Labels
question Further information is requested

Comments

@fsnlarson
Copy link

This may be helpful for people.

The paginate function runs into severe performance issues when the datasets are in the thousands.

We are using fastapi_pagination with SQLAlchemy and with a result of 8000 records. Since paginate requires a Sequence we must load the entire query result into memory and then paginate slices the result. Avg. response time above 1 second.

paginate function in the package.

def paginate(
    sequence: Sequence[T],
    params: Optional[AbstractParams] = None,
    length_function: Callable[[Sequence[T]], int] = len,
) -> AbstractPage[T]:
    params = resolve_params(params)
    raw_params = params.to_raw_params()

    return create_page(
        items=sequence[raw_params.offset : raw_params.offset + raw_params.limit],
        total=length_function(sequence),
        params=params,
    )

Our solution was to do the following, which uses the native offset() and limit() from SQLAlchemy. Obviously this does not apply to other databases ORMs so a better approach would be potentially allowing a slice_function like the length_function and then allowing a queryset-type object to be passed instead of only a sequence.

Our solution

def paginate(
    query: sqlalchemy.orm.query.Query,
    params: Optional[AbstractParams] = None,
) -> AbstractPage[T]:
    params = resolve_params(params)
    raw_params = params.to_raw_params()

    return create_page(
        items=query.offset(params.offset).limit(params.limit).all(),
        total=query.count(),
        params=params,
    )

The new function is 100x faster (.01 seconds)

@uriyyo uriyyo self-assigned this Jan 27, 2022
@uriyyo uriyyo added the question Further information is requested label Jan 27, 2022
@uriyyo
Copy link
Owner

uriyyo commented Jan 27, 2022

Hi @fsnlarson,
Did you try to use fastapi_pagination.ext.sqlalchemy.paginate? It looks almost the same as your solution:

def paginate_query(query: T, params: AbstractParams) -> T:
raw_params = params.to_raw_params()
return query.limit(raw_params.limit).offset(raw_params.offset)
def _to_dict(obj: Any) -> Any:
try:
return obj._asdict()
except AttributeError:
return obj
def paginate(query: Query, params: Optional[AbstractParams] = None) -> AbstractPage:
params = resolve_params(params)
total = query.count()
items = [_to_dict(item) for item in paginate_query(query, params)]
return create_page(items, total, params)

@fsnlarson
Copy link
Author

That extension is the right solution. The docs should maybe be updated to show an example of using an extension.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants