fix(evaluator): recover cache when cve gets re-imported - #2459
Conversation
Reviewer's GuideAdds a recovery path in evaluator logic for stale CVE cache entries that cause foreign key violations on insert, and introduces focused tests to validate the cache eviction and retry behavior as well as correct error propagation. Sequence diagram for evaluator CVE cache recovery on foreign key violationsequenceDiagram
participant Evaluator
participant Conn as AsyncConnection
participant DB as Database
Evaluator->>Evaluator: _evaluate_vmaas_res(to_insert, to_delete, conn)
alt has_rows_to_insert
Evaluator->>Evaluator: _insert_vulnerable_package_cve_with_recovery(to_insert, conn)
rect rgb(240,240,240)
Evaluator->>Conn: transaction()
Evaluator->>DB: _insert_vulnerable_package_cve(to_insert, conn)
alt ForeignKeyViolation on cve_id
DB-->>Evaluator: psycopg_errors.ForeignKeyViolation
Evaluator->>Evaluator: refresh_cve_cache_from_id_to_name
loop for each (vpid, cve_id) in to_insert
Evaluator->>Evaluator: cve_cache.pop(name)
Evaluator->>DB: _get_or_upsert_cve(name)
DB-->>Evaluator: cve_with_fresh_id
end
Evaluator->>DB: _insert_vulnerable_package_cve(refreshed, conn)
DB-->>Evaluator: insert_success
else no ForeignKeyViolation
DB-->>Evaluator: insert_success
end
end
end
alt has_rows_to_delete
Evaluator->>DB: _delete_vulnerable_package_cve(to_delete, conn)
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Accessing
e.diag.constraint_nameassumesdiagis always present; consider defensively checkinghasattr(e, "diag")and thate.diag.constraint_nameis notNonebefore comparing to avoid attribute errors in unexpected psycopg error shapes. - The first insert attempt is wrapped in
conn.transaction()but the recovery insert is not, which changes transactional behavior compared to the original_insert_vulnerable_package_cve; consider making the second attempt use the same transaction semantics for consistency and atomicity.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Accessing `e.diag.constraint_name` assumes `diag` is always present; consider defensively checking `hasattr(e, "diag")` and that `e.diag.constraint_name` is not `None` before comparing to avoid attribute errors in unexpected psycopg error shapes.
- The first insert attempt is wrapped in `conn.transaction()` but the recovery insert is not, which changes transactional behavior compared to the original `_insert_vulnerable_package_cve`; consider making the second attempt use the same transaction semantics for consistency and atomicity.
## Individual Comments
### Comment 1
<location path="evaluator/logic.py" line_range="434-439" />
<code_context>
to_insert,
)
+ async def _insert_vulnerable_package_cve_with_recovery(self, to_insert: List[Tuple[int, int]], conn: AsyncConnection):
+ """Insert vulnerable_package_cve rows, recovering from stale CVE cache"""
+ try:
+ async with conn.transaction():
+ await self._insert_vulnerable_package_cve(to_insert, conn)
+ except psycopg_errors.ForeignKeyViolation as e:
+ if e.diag.constraint_name != "cve_id":
+ raise
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against missing/None `diag.constraint_name` when inspecting the ForeignKeyViolation.
This code assumes `e.diag` and `diag.constraint_name` are always set, which isn’t guaranteed. If `e.diag` is `None` or lacks `constraint_name`, the recovery logic will raise `AttributeError` instead of correctly handling the foreign key violation. Consider guarding access, e.g. `if getattr(e.diag, "constraint_name", None) != "cve_id": raise`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
482d1e6 to
37cc48e
Compare
jdobes
left a comment
There was a problem hiding this comment.
I don't like this solution very much because it's hijacking the foreign key violation handling on specific table inserts. The issue can generally happen every table with cve_id foreign key. I'd prefer more systematic solution similar to the rule_id cache refresh mechanism:
- Once every few minutes rebuild the whole CVE cache
- Since CVE cache contains about 1000x more items than rule_id cache, we can track when the last vmaas_sync ran and re-build the cache after that (since the bug can happen only after vmaas-sync removes a CVE)
Evaluators CVE cache is now able to recover for re-imported CVE. RHINENG-29405
37cc48e to
26b8d58
Compare
|
Okay cool, I got this. This approach incurs a slightly higher DB load (though the Edit: Switched from guard to polling strategy and increased the ttl time. |
Shrink cache metedata into single dict to achieve more compact approach and so the pylint does not complain as well. Improved tests.
26b8d58 to
ccfe21f
Compare
vmaas sync does not happen that often and this is just a safeguard rather than keeping cache warm scenario, so we can keep the less frequent refresh strategy and lower db load. Tests updated.
Evaluators CVE cache is now able to recover for re-imported CVE. RHINENG-29405
Secure Coding Practices Checklist GitHub Link
Secure Coding Checklist
Summary by Sourcery
Refresh the evaluator CVE cache after completed VMAAS CVE imports while avoiding unnecessary database reloads through configurable cache expiration.
New Features:
Bug Fixes:
Enhancements:
Deployment:
Tests: