Skip to content

OSIDB-4738: Add context to async process in history.#1213

Merged
roduran-dev merged 1 commit intomasterfrom
OSIDB-4738_multi_instance_change
Apr 29, 2026
Merged

OSIDB-4738: Add context to async process in history.#1213
roduran-dev merged 1 commit intomasterfrom
OSIDB-4738_multi_instance_change

Conversation

@roduran-dev
Copy link
Copy Markdown
Contributor

@roduran-dev roduran-dev commented Feb 27, 2026

This PR adds a bit more context for the async tasks in async manager. The idea is to have a better view on events conflicts.

It also adds more contexts into the history for the sync_manager making easier to understand if a celery process did a change.

Example of how context looks (First lines are empty because Flaw was created with custom code)

 - 2026-02-27 16:42:00.664918+00:00
 - 2026-02-27 16:42:00.806890+00:00
**************************************************
None
**************************************************
 - 2026-02-27 16:42:00.890381+00:00
**************************************************
None
**************************************************
 - 2026-02-27 16:43:06.290773+00:00
**************************************************
{'path': '/osidb/api/v2/flaws/4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'user': 'monke@banana.com'}
**************************************************
 - 2026-02-27 16:43:06.915591+00:00
**************************************************
{'path': '/osidb/api/v1/flaws/CVE-2026-10007/promote', 'user': 'monke@banana.com'}
**************************************************
 - 2026-02-27 16:43:07.542939+00:00
**************************************************
{'path': '/osidb/api/v2/flaws/4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'user': 'monke@banana.com'}
**************************************************
OSIM-74136 - 2026-02-27 16:43:10.760294+00:00
**************************************************
{'action': 'jira_task_sync', 'source': 'celery', 'flaw_uuid': '4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'celery_task_id': '1ff25c35-c5be-47a2-a036-8e43592aefbc'}
**************************************************
OSIM-74137 - 2026-02-27 16:43:12.808267+00:00
**************************************************
{'action': 'jira_task_sync', 'source': 'celery', 'flaw_uuid': '4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'celery_task_id': '48b5b426-1d9e-4ecb-8443-b63248f22580'}
**************************************************
OSIM-74138 - 2026-02-27 16:43:16.493056+00:00
**************************************************
{'action': 'jira_task_sync', 'source': 'celery', 'flaw_uuid': '4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'celery_task_id': 'c21c62d2-c6c9-462d-8d9e-5f62d1c65cc6'}
**************************************************
OSIM-74138 - 2026-02-27 16:43:33.640337+00:00
**************************************************
{'path': '/osidb/api/v2/flaws/4b32861b-6271-4e35-a0a0-fb991cf3e8bd', 'user': 'monke@banana.com'}
**************************************************
OSIM-74138 - 2026-02-27 16:43:34.152565+00:00
**************************************************
{'path': '/osidb/api/v1/flaws/CVE-2026-10007/promote', 'user': 'monke@banana.com'}
**************************************************

Closes OSIDB-4738

@roduran-dev roduran-dev added the technical For PRs that introduce changes not worthy of a CHANGELOG entry label Feb 27, 2026
Comment thread osidb/mixins.py Outdated
last_event_user = self._get_last_event_context_user()
actual_event_user = self._get_actual_user()

logger.info(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Im not sure if it should be info, error or debug.

@roduran-dev roduran-dev requested a review from a team February 27, 2026 17:23
@roduran-dev roduran-dev force-pushed the OSIDB-4738_multi_instance_change branch from ab6bc7a to c3fc47c Compare February 27, 2026 17:24
@roduran-dev roduran-dev removed the request for review from a team March 2, 2026 09:31
@roduran-dev roduran-dev force-pushed the OSIDB-4738_multi_instance_change branch 5 times, most recently from 8683b18 to 369072a Compare March 2, 2026 13:46
@roduran-dev roduran-dev requested a review from a team March 2, 2026 14:27
Comment thread osidb/mixins.py Outdated
Comment thread osidb/mixins.py
Comment on lines +107 to +113
def _get_actual_user(self):
ctx = getattr(
pghistory.runtime._tracker, "value", None
) # active context for this thread, if any
actual_user = (ctx.metadata.get("user") if ctx else None) or "unknown"
return actual_user

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.

the user is already logged on the access logs (check for /var/log/(prod|stage|uat)-access.log on splunk)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm getting the user so people can tell from the error why their instance is outdated without having to go to the history or the logs.

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 not sure I follow the logic of adding the user here. If the message is for the person making the change, would _get_actual_user not just return that person?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, is true. This was a leftover from the first change where I took the last change user from history to explain the user who or what changed the model.

@costaconrado now that the history is indexed, is it safe to take the last changed from history?

if not it true that it doesn't really make sense.

Comment thread osidb/mixins.py Outdated
@roduran-dev roduran-dev force-pushed the OSIDB-4738_multi_instance_change branch from 369072a to 4cd3ae7 Compare March 12, 2026 10:15
@roduran-dev roduran-dev requested review from a team March 12, 2026 16:20
@roduran-dev roduran-dev self-assigned this Mar 12, 2026
@roduran-dev
Copy link
Copy Markdown
Contributor Author

Thanks for review @costaconrado 😄

I deleted to part you told me about so it should be no longer a problem. Could you give it a second look?

Copy link
Copy Markdown
Contributor

@costaconrado costaconrado left a comment

Choose a reason for hiding this comment

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

LGTM.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved error messages for data inconsistencies now include user and timestamp information with references to audit history for better troubleshooting.
  • Improvements

    • Enhanced tracking and attribution of bulk data changes to better document the source of modifications.

Walkthrough

Added pghistory context management to Celery-driven sync flows for history attribution and updated TrackingMixin to compute and include the actual event user (from per-thread pghistory runtime metadata) in outdated-instance DataInconsistencyException messages.

Changes

Cohort / File(s) Summary
Tracking mixin
osidb/mixins.py
Added TrackingMixin._get_actual_user() which reads pghistory.runtime._tracker.value.metadata["user"] with a fallback to "unknown". TrackingMixin.save() now uses that user and the DB updated_dt in the DataInconsistencyException message (replacing the prior static message).
Sync manager & pghistory integration
osidb/sync_manager.py
Added module-level pghistory_context(...) context manager to build pghistory.context from source, user, action, extra_context, and conditional celery_task_id. BZTrackerDownloadManager.link_tracker_with_affects() signature changed to accept task (default "UNKNOWN") and its affects update is wrapped in pghistory_context. Various Celery-driven sync methods (BZTrackerDownloadManager.sync_task, JiraTaskSyncManager.sync_task, JiraTaskTransitionManager.sync_task, JiraTrackerDownloadManager.sync_task) now enter pghistory_context (passing action and celery_task_id when available) around DB fetch/save/transition operations.

Sequence Diagram

sequenceDiagram
    actor Celery as Celery Task
    participant SyncMgr as Sync Manager
    participant PGHist as pghistory_context
    participant Model as Model (Flaw/Tracker)
    participant DB as Database

    Celery->>SyncMgr: sync_task(task)
    SyncMgr->>PGHist: enter context(action, user, celery_task_id)
    PGHist->>PGHist: set pghistory.context(...)
    SyncMgr->>Model: get / modify instance
    Model->>DB: save() (within pghistory context)
    DB->>DB: persist record + pghistory metadata
    SyncMgr->>PGHist: exit context
    PGHist->>PGHist: clear pghistory.context
    SyncMgr-->>Celery: task complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding context (via pghistory_context helper and _get_actual_user method) to async/Celery processes in history tracking.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation for adding async task context to history for better visibility into event conflicts and Celery process attribution.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch OSIDB-4738_multi_instance_change

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@roduran-dev roduran-dev requested a review from Jincxz April 14, 2026 12:54
Copy link
Copy Markdown

@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

♻️ Duplicate comments (1)
osidb/mixins.py (1)

75-82: ⚠️ Potential issue | 🟠 Major

Keep user identifiers out of the conflict exception text.

This pulls the actor from pghistory metadata and injects it into a DataInconsistencyException. osidb/exception_handlers.py then logs str(exc) and returns it verbatim, while osidb/middleware.py shows that user can be an email/name. That turns a normal 409 into an application-log/API echo of a user identifier. The actor is already available in history metadata; it should not be serialized into the exception message.

🔧 Suggested change
-                actual_event_user = self._get_actual_user()
-
                 message = (
-                    f"Save operation based on an outdated model instance by "
-                    f"{actual_event_user} with updated_dt {self.updated_dt} "
-                    f"differs from the DB {db_self.updated_dt} check in history for details."
+                    f"Save operation based on an outdated model instance with "
+                    f"updated_dt {self.updated_dt} differs from the DB "
+                    f"{db_self.updated_dt}; check history for details."
                 )
                 raise DataInconsistencyException(f"{message} You need to refresh.")
-
-    def _get_actual_user(self):
-        ctx = getattr(
-            pghistory.runtime._tracker, "value", None
-        )  # active context for this thread, if any
-        actual_user = (ctx.metadata.get("user") if ctx else None) or "unknown"
-        return actual_user

Also applies to: 91-96

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osidb/mixins.py` around lines 75 - 82, The exception message currently
includes the actor identifier retrieved by _get_actual_user()
(actual_event_user) and is raised as a DataInconsistencyException; remove the
actor from the serialized exception text and replace it with a generic message
(e.g. "Save operation based on an outdated model instance; refresh and retry")
while keeping the actor available only in pghistory metadata for internal logs;
update both the block that builds message using self.updated_dt vs
db_self.updated_dt and the similar block around lines 91-96 to stop
interpolating actual_event_user into the exception string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@osidb/sync_manager.py`:
- Around line 1025-1035: The code calls
JiraTrackerDownloadManager.link_tracker_with_affects(tracker_id) twice (once
inside the pghistory_context block when tracker exists and again afterward),
causing duplicate writes and audit entries; remove the redundant call outside
the pghistory_context and ensure link_tracker_with_affects(tracker_id) is
invoked only once inside the pghistory_context where tracker.save() runs so the
relation changes are recorded under the intended pghistory_context (locate
usages of JiraTrackerDownloadManager.link_tracker_with_affects and the
surrounding pghistory_context/celery_task_id block to make this change).

---

Duplicate comments:
In `@osidb/mixins.py`:
- Around line 75-82: The exception message currently includes the actor
identifier retrieved by _get_actual_user() (actual_event_user) and is raised as
a DataInconsistencyException; remove the actor from the serialized exception
text and replace it with a generic message (e.g. "Save operation based on an
outdated model instance; refresh and retry") while keeping the actor available
only in pghistory metadata for internal logs; update both the block that builds
message using self.updated_dt vs db_self.updated_dt and the similar block around
lines 91-96 to stop interpolating actual_event_user into the exception string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ae6982c3-730e-4bf1-915d-21bad8981824

📥 Commits

Reviewing files that changed from the base of the PR and between 00768b8 and 4de58f0.

📒 Files selected for processing (2)
  • osidb/mixins.py
  • osidb/sync_manager.py

Comment thread osidb/sync_manager.py
Copy link
Copy Markdown
Contributor

@Jincxz Jincxz left a comment

Choose a reason for hiding this comment

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

LGTM, just some comments.

This PR is for adding logging correct? Is / was there one for handling the sync issue mentioned in OSIDB-4738?

Comment thread osidb/mixins.py
Comment on lines +107 to +113
def _get_actual_user(self):
ctx = getattr(
pghistory.runtime._tracker, "value", None
) # active context for this thread, if any
actual_user = (ctx.metadata.get("user") if ctx else None) or "unknown"
return actual_user

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 not sure I follow the logic of adding the user here. If the message is for the person making the change, would _get_actual_user not just return that person?

Comment thread osidb/sync_manager.py
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
osidb/sync_manager.py (2)

20-34: Type hint mismatch: celery_task_id can be None.

The parameter is typed as str but Line 30 explicitly handles falsy values, and all call sites pass getattr(getattr(task, "request", None), "id", None) which can return None.

Suggested fix
 `@contextmanager`
 def pghistory_context(
     action: str,
-    celery_task_id: str,
+    celery_task_id: Optional[str],
     *,
     source: str = "celery",
     user: str = "celery_task",
     **extra_context: Any,
 ) -> Iterator[None]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osidb/sync_manager.py` around lines 20 - 34, The pghistory_context signature
types celery_task_id as str but callers can pass None; update the function to
accept Optional[str] (i.e., change celery_task_id: str to celery_task_id:
Optional[str]) and add Optional to the typing imports so the runtime None check
(if celery_task_id:) remains valid; leave the body and pghistory.context usage
unchanged.

1004-1007: Inconsistent implementation: no pghistory context wrapping.

Unlike BZTrackerDownloadManager.link_tracker_with_affects (lines 582-588), this method does not accept a task parameter and does not wrap its atomic block in pghistory_context. This creates inconsistent history tracking between Bugzilla and Jira tracker syncs.

Consider either:

  1. Having the caller wrap this call in pghistory_context (requires fixing lines 1025-1031), or
  2. Adding the task parameter and internal wrapping to match the BZ implementation
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osidb/sync_manager.py` around lines 1004 - 1007, This method updates
tracker.affects inside a transaction.atomic() without using pghistory_context,
causing inconsistent pghistory entries compared to
BZTrackerDownloadManager.link_tracker_with_affects; add a task parameter to this
method and wrap the transaction.atomic() block in pghistory_context(task=task)
(mirroring BZTrackerDownloadManager.link_tracker_with_affects) so that
tracker.affects.clear(), tracker.affects.add(*affects) and tracker.save(...) are
executed inside the pghistory_context; alternatively, if you prefer the caller
to control context, update the caller code path (the code around lines
1025-1031) to wrap this call in pghistory_context — but prefer the former for
parity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@osidb/sync_manager.py`:
- Around line 20-34: The pghistory_context signature types celery_task_id as str
but callers can pass None; update the function to accept Optional[str] (i.e.,
change celery_task_id: str to celery_task_id: Optional[str]) and add Optional to
the typing imports so the runtime None check (if celery_task_id:) remains valid;
leave the body and pghistory.context usage unchanged.
- Around line 1004-1007: This method updates tracker.affects inside a
transaction.atomic() without using pghistory_context, causing inconsistent
pghistory entries compared to
BZTrackerDownloadManager.link_tracker_with_affects; add a task parameter to this
method and wrap the transaction.atomic() block in pghistory_context(task=task)
(mirroring BZTrackerDownloadManager.link_tracker_with_affects) so that
tracker.affects.clear(), tracker.affects.add(*affects) and tracker.save(...) are
executed inside the pghistory_context; alternatively, if you prefer the caller
to control context, update the caller code path (the code around lines
1025-1031) to wrap this call in pghistory_context — but prefer the former for
parity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0cd0cff1-a627-4c1e-a46f-af153b6119af

📥 Commits

Reviewing files that changed from the base of the PR and between 4de58f0 and a512512.

📒 Files selected for processing (1)
  • osidb/sync_manager.py

@roduran-dev
Copy link
Copy Markdown
Contributor Author

roduran-dev commented Apr 20, 2026

@Jincxz to better answer your question:

This issue is for several reasons that touch at the point of multi instance.

  1. Conflicts that the user don't see.
    • The async process makes a change so the model in memory for the user is not valid anymore.
  2. Revert changes
    - Seems like The async process finish the process with an idle process but I don't know which one, this will allow to have a better understanding of what was the change.

This will give a better insight for the history on the async processes.

@roduran-dev roduran-dev changed the title OSIDB-4738: multi instance conflict context OSIDB-4738: Add context to async process in history. Apr 20, 2026
Copy link
Copy Markdown
Contributor

@Jincxz Jincxz left a comment

Choose a reason for hiding this comment

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

LGTM, I would squash some of these commits.

@roduran-dev roduran-dev force-pushed the OSIDB-4738_multi_instance_change branch from a512512 to b93fb33 Compare April 22, 2026 08:10
Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@osidb/sync_manager.py`:
- Around line 1024-1032: The Jira flow loses pghistory attribution because
JiraTrackerDownloadManager.link_tracker_with_affects does not accept the task
nor wrap the affects clear/add/save in pghistory_context; update
JiraTrackerDownloadManager.link_tracker_with_affects to accept a task parameter,
wrap the tracker.affects.clear()/add()/save() block with
pghistory_context(action="link_tracker_with_affects",
celery_task_id=getattr(getattr(task, "request", None), "id", None)), and then
modify the caller in sync_task to pass the current task through when calling
JiraTrackerDownloadManager.link_tracker_with_affects(tracker_id, task) so
history entries include source/action/celery_task_id.
- Around line 20-33: The pghistory_context signature declares celery_task_id as
str but callers pass None; update the parameter type to Optional[str] (and
import Optional if needed) so it becomes celery_task_id: Optional[str], keeping
the existing conditional handling (if celery_task_id:) and no other logic
changes; reference the pghistory_context function to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 019dc08c-dd3e-4470-92da-009343a9d77e

📥 Commits

Reviewing files that changed from the base of the PR and between a512512 and b93fb33.

📒 Files selected for processing (2)
  • osidb/mixins.py
  • osidb/sync_manager.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • osidb/mixins.py

Comment thread osidb/sync_manager.py
Comment on lines +20 to +33
@contextmanager
def pghistory_context(
action: str,
celery_task_id: str,
*,
source: str = "celery",
user: str = "celery_task",
**extra_context: Any,
) -> Iterator[None]:
ctx = {"source": source, "user": user, "action": action, **extra_context}
if celery_task_id:
ctx["celery_task_id"] = celery_task_id
with pghistory.context(**ctx):
yield
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

celery_task_id should be typed as Optional[str].

Every call site passes getattr(getattr(task, "request", None), "id", None), which can legitimately return None, and the body already handles that case with if celery_task_id:. Declaring the parameter as str misrepresents the contract and will trip type checkers/IDE warnings on every caller.

Proposed fix
 `@contextmanager`
 def pghistory_context(
     action: str,
-    celery_task_id: str,
+    celery_task_id: Optional[str],
     *,
     source: str = "celery",
     user: str = "celery_task",
     **extra_context: Any,
 ) -> Iterator[None]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osidb/sync_manager.py` around lines 20 - 33, The pghistory_context signature
declares celery_task_id as str but callers pass None; update the parameter type
to Optional[str] (and import Optional if needed) so it becomes celery_task_id:
Optional[str], keeping the existing conditional handling (if celery_task_id:)
and no other logic changes; reference the pghistory_context function to locate
the change.

Comment thread osidb/sync_manager.py
Comment on lines 1024 to 1032
if tracker:
tracker.save()
with pghistory_context(
action="jira_tracker_download",
celery_task_id=getattr(getattr(task, "request", None), "id", None),
):
tracker.save()

# Link this Jira tracker to OSIDB Affects as part of the sync process.
result = JiraTrackerDownloadManager.link_tracker_with_affects(tracker_id)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 22, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Jira tracker path loses pghistory attribution for the affects re-link — inconsistent with the BZ flow.

For the BZ flow, BZTrackerDownloadManager.link_tracker_with_affects itself opens a pghistory_context(action="link_tracker_with_affects", celery_task_id=...) around the clear()/add()/save() (lines 581‑588), and sync_task forwards task into it (lines 605‑607). For the Jira flow:

  1. JiraTrackerDownloadManager.link_tracker_with_affects (lines 1004‑1007) performs the same tracker.affects.clear()/add()/save() with no pghistory_context wrapping at all.
  2. Here, link_tracker_with_affects(tracker_id) is called outside the pghistory_context block at line 1032 and is not passed task.

Net effect: every Jira-driven tracker re-link writes history entries without source=celery / action / celery_task_id, which is exactly the attribution gap this PR is meant to close. Please mirror the BZ approach — accept task in JiraTrackerDownloadManager.link_tracker_with_affects and wrap its atomic block in pghistory_context, then forward task from sync_task.

Proposed fix
     `@staticmethod`
-    def link_tracker_with_affects(tracker_id):
+    def link_tracker_with_affects(tracker_id, task=None):
         # Code adapted from collectors.jiraffe.convertors.JiraTrackerConvertor.affects
         ...
         with transaction.atomic():
-            tracker.affects.clear()
-            tracker.affects.add(*affects)
-            tracker.save(raise_validation_error=False, auto_timestamps=False)
+            with pghistory_context(
+                action="link_tracker_with_affects",
+                celery_task_id=getattr(getattr(task, "request", None), "id", None),
+            ):
+                tracker.affects.clear()
+                tracker.affects.add(*affects)
+                tracker.save(raise_validation_error=False, auto_timestamps=False)

And in sync_task:

-            # Link this Jira tracker to OSIDB Affects as part of the sync process.
-            result = JiraTrackerDownloadManager.link_tracker_with_affects(tracker_id)
+            # Link this Jira tracker to OSIDB Affects as part of the sync process.
+            result = JiraTrackerDownloadManager.link_tracker_with_affects(
+                tracker_id, task
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osidb/sync_manager.py` around lines 1024 - 1032, The Jira flow loses
pghistory attribution because
JiraTrackerDownloadManager.link_tracker_with_affects does not accept the task
nor wrap the affects clear/add/save in pghistory_context; update
JiraTrackerDownloadManager.link_tracker_with_affects to accept a task parameter,
wrap the tracker.affects.clear()/add()/save() block with
pghistory_context(action="link_tracker_with_affects",
celery_task_id=getattr(getattr(task, "request", None), "id", None)), and then
modify the caller in sync_task to pass the current task through when calling
JiraTrackerDownloadManager.link_tracker_with_affects(tracker_id, task) so
history entries include source/action/celery_task_id.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was left like this so only when an actual change in the tracker happens it will use the context.

The linking is happening always so it will only create noise.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@roduran-dev, understood — the deliberate choice to limit pghistory_context to the conditional tracker.save() block (where an actual tracker change occurs) and exclude the always-running link_tracker_with_affects call makes sense. Annotating routine/unconditional operations would reduce the signal-to-noise ratio of the history context. I'll withdraw the suggestion.


📝 Learnings were identified and not saved (knowledge base disabled). Enable

@roduran-dev roduran-dev added this pull request to the merge queue Apr 22, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Apr 22, 2026
@roduran-dev roduran-dev added this pull request to the merge queue Apr 29, 2026
Merged via the queue into master with commit 7b84bca Apr 29, 2026
11 checks passed
@roduran-dev roduran-dev deleted the OSIDB-4738_multi_instance_change branch April 29, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

technical For PRs that introduce changes not worthy of a CHANGELOG entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants