Skip to content

fix(api): add bulk_upsert method and unit tests for opensearch BaseOSDB class - #990

Draft
vkuznet wants to merge 18 commits into
DIRACGrid:mainfrom
vkuznet:fix/issue-401
Draft

vkuznet wants to merge 18 commits into
DIRACGrid:mainfrom
vkuznet:fix/issue-401

Conversation

@vkuznet

@vkuznet vkuznet commented Jul 14, 2026

Copy link
Copy Markdown

This PR fixes issue #401 by introducing the bulk_upsert API to BaseOSDB class. It is also complement by full set of unit tests for BaseOSDB class which were missing. The unit test introduces mock client and different classes for different use cases.

Depends on #1007

Please note: it is my first PR and I'm happy to adjust it according to requirements/guidelines of DiracX community.

@aldbr aldbr linked an issue Jul 15, 2026 that may be closed by this pull request
@read-the-docs-community

read-the-docs-community Bot commented Jul 16, 2026

Copy link
Copy Markdown

@fstagni fstagni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the original issue (#401) it was mentioned a specific use case. Can you already verify if this fix covers that? You can add it directly in this PR.

Comment thread diracx-db/src/diracx/db/os/utils.py
@vkuznet

vkuznet commented Aug 6, 2026

Copy link
Copy Markdown
Author

In the original issue (#401) it was mentioned a specific use case. Can you already verify if this fix covers that? You can add it directly in this PR.

@fstagni regarding issue #401. I checked the main repository code and what is described in this issue is not there the issue is based on commit outside of any branch, most likely in fork outside of the repository

# TODO: can we upsert to multiple documents?
for job_id, p_updates_ in param_updates.items():
if p_updates_:
await job_parameters_db.upsert(
int(job_id),
p_updates_,
)
). Therefore, before adding anything to this PR with usage of bulk insert I need to know if it is in fact required by code from main repository, see utils.py from main branch

@vkuznet vkuznet self-assigned this Aug 6, 2026
@aldbr
aldbr requested a review from fstagni August 6, 2026 14:36
@fstagni

fstagni commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Hi, since #410 was created, the mentioned code has been moved and refactored, and can now be found in https://github.com/DIRACGrid/diracx/blob/main/diracx-logic/src/diracx/logic/jobs/status.py#L643. The issue anyway still holds, can you have a look?

@aldbr aldbr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @vkuznet!
Just a few additional comments, sorry for the delay we take to review PRs...

response,
)

async def bulk_upsert(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

bulk_upsert would need to overridden within job_parameters_db because we are inserting a JobID and a timestamp:

def upsert(self, vo, doc_id, document):
document = {
"JobID": doc_id,
"timestamp": int(datetime.now(tz=UTC).timestamp() * 1000),
**document,
}
return super().upsert(vo, doc_id, document)

Here it would not work I think (and it looks like it's not spotted within the tests).

I actually wonder whether upsert is useful now that we have bulk_upsert.
I would suggest we just drop upsert and replace it everywhere with bulk_upsert, what do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is fixed now in 6d2984c

But I against dropping upsert in favor of bulk_upsert for simple reason. The code, e.g. https://github.com/DIRACGrid/diracx/blob/main/diracx-logic/src/diracx/logic/jobs/status.py#L225, uses external for loop and insert each document individually while going through that loop. To use bulk_upsert would require in this place to either use generator or allocate more memory to collect all documents and then insert them in bulk. There are cases when one API is preferable vs another. Since I can't find usage of generators I think the upsert has its place in a code.

if new_application:
job_data["ApplicationStatus"] = new_application

await job_parameters_db.upsert(res["VO"], job_id, {"Status": new_status})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

bulk_upsert could (should) be used here too I think

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It can but it would either refactor code to use generators or use additional memory allocation to collect all documents. Either task seems beyond this PR scope.

self.client,
actions,
raise_on_error=False,
raise_on_exception=False,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm just wondering what happens if there is a connection issue with the DB and no exception is raised.
I guess you would get 0 success, N errors but would get any information to know that there is an issue with the DB itself?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

in order to use this features it would be desired to have them configurable rather using hard-coded defaults. At the moment (based on my limited scope of the code) I don't know how configuration work and if desired this can be done through a separate issue/PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm just wondering what happens if there is a connection issue with the DB and no exception is raised. I guess you would get 0 success, N errors but would get any information to know that there is an issue with the DB itself?

this is up to upstream code. Since it is external we can't reliably tell what is current and future functionality would be.

Comment on lines +683 to +686
if errors:
for error in errors:
logger.error("bulk insert error %s", error)
raise DocumentUpsertError("Failed to perform bulk insert operation")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I assume this piece of code is generic because will be reused every time there is an error.
Wouldn't it make sense to raise the DocumentUpsertError from diracx-db itself? So that this part of the code only lives in db/os/utils and is automatically reused by the callers

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this is architectural choice, your suggestion to through exception in base bulk_upsert API while mine is to pass errors to upstream and let this code decide what to do. If you can provide specific use-case when exception is better I'll be happy to move this part down the stream and through exception in a base class.

Comment thread diracx-db/tests/utils/test_utils.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread diracx-logic/tests/conftest.py Outdated
Comment thread diracx-db/tests/conftest.py Outdated
@DIRACGridBot
DIRACGridBot marked this pull request as draft September 2, 2026 11:05
@vkuznet
vkuznet requested a review from aldbr September 13, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support upserts to multiple documents for opensearch

3 participants