Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
586d311
fix(api): add bulk_upsert method and unit tests for opensearch BaseOS…
vkuznet Jul 14, 2026
35dd659
fix(api): adjust failed unit tests
vkuznet Jul 14, 2026
fe52fc2
Merge branch 'main' into fix/issue-401
vkuznet Jul 16, 2026
14f9515
Merge branch 'main' into fix/issue-401
vkuznet Aug 10, 2026
cfb5e53
Merge branch 'main' into fix/issue-401
vkuznet Aug 17, 2026
f22047f
Merge branch 'main' into fix/issue-401
vkuznet Aug 18, 2026
0775e3d
Merge branch 'main' into fix/issue-401
vkuznet Aug 24, 2026
0886112
Merge branch 'main' into fix/issue-401
vkuznet Aug 31, 2026
497583a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 31, 2026
0f15cd2
fix(imports): sort import errors
vkuznet Aug 31, 2026
392c88b
refactor: move common mock_client to diracx-testing and use it as needed
vkuznet Aug 31, 2026
0ac5dba
refactor: put back mock_client into appropriate tests
vkuznet Aug 31, 2026
1c66216
refactor: move mock_client into testing module; add bulk_upsert imple…
vkuznet Aug 31, 2026
48474ea
refactor: use mock_client via common mock_osdb module and conftest.py…
vkuznet Aug 31, 2026
6d2984c
fix: add bulk_upsert into job_parameters
vkuznet Sep 9, 2026
c2b68aa
fix: reuse DummyOSDB from diracx.testing
vkuznet Sep 9, 2026
188948d
fix: put diracx.testing.mock_osdb into pyproject.toml instead of keep…
vkuznet Sep 9, 2026
7dc18e9
Merge branch 'main' into fix/issue-401
vkuznet Sep 9, 2026
979833c
Merge branch 'main' into fix/issue-401
vkuznet Sep 17, 2026
eacac9a
fix: put back conftest to ensure that diracx-db/tests/utils/test_util…
vkuznet Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion diracx-db/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ testpaths = ["tests"]
addopts = [
"-v",
"--cov=diracx.db", "--cov-report=term-missing",
"-pdiracx.testing", "-pdiracx.testing.osdb",
"-pdiracx.testing", "-pdiracx.testing.osdb", "-pdiracx.testing.mock_osdb",
"--import-mode=importlib",
]
asyncio_mode = "auto"
Expand Down
13 changes: 13 additions & 0 deletions diracx-db/src/diracx/db/os/job_parameters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from datetime import UTC, datetime
from typing import Any, Iterable

from diracx.db.os.utils import BaseOSDB

Expand Down Expand Up @@ -41,3 +42,15 @@ def upsert(self, vo, doc_id, document):
**document,
}
return super().upsert(vo, doc_id, document)

async def bulk_upsert(
self,
documents: Iterable[tuple[str, int, dict[str, Any]]],
) -> tuple[int, list[Any]]:
"""bulk_upsert API implementation."""
transformed = []
for vo, doc_id, document in documents:
timestamp = int(datetime.now(tz=UTC).timestamp() * 1000)
document = {"JobID": doc_id, "timestamp": timestamp, **document}
transformed.append((vo, doc_id, document))
return await super().bulk_upsert(transformed)
32 changes: 31 additions & 1 deletion diracx-db/src/diracx/db/os/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import json
import logging
from abc import ABCMeta, abstractmethod
from collections.abc import AsyncIterator
from collections.abc import AsyncIterator, Iterable
from contextvars import ContextVar
from datetime import datetime
from typing import Any, Self

from opensearchpy import AsyncOpenSearch
from opensearchpy.exceptions import RequestError
from opensearchpy.helpers import async_bulk

from diracx.core.exceptions import DocumentUpsertError, InvalidQueryError
from diracx.core.extensions import DiracEntryPoint, select_from_extension
Expand Down Expand Up @@ -213,6 +214,35 @@ async def upsert(self, vo: str, doc_id: int, document: Any) -> None:
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.

self,
documents: Iterable[tuple[str, int, dict[str, Any]]],
) -> tuple[int, list[Any]]:
"""Bulk upsert documents."""
actions = (
{
"_op_type": "update",
"_index": self.index_name(vo, doc_id),
"_id": doc_id,
"doc": document,
"doc_as_upsert": True,
"retry_on_conflict": 10,
}
for vo, doc_id, document in documents
)

success, errors = await async_bulk(
self.client,
actions,
raise_on_error=False,
raise_on_exception=False,
Comment thread
aldbr marked this conversation as resolved.

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.

)

if errors:
logger.warning("Bulk upsert completed with %d errors", len(errors))

return success, errors

async def search(
self, parameters, search, sorts, *, per_page: int = 100, page: int | None = None
) -> list[dict[str, Any]]:
Expand Down
3 changes: 3 additions & 0 deletions diracx-db/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from __future__ import annotations

pytest_plugins = ["diracx.testing.mock_osdb"]
Loading
Loading