Migrate `Vulnerability::Read` create / update logic from postgresql to Rails
<!--Implementation issues are used break-up a large piece of work into small, discrete tasks that can
move independently through the build workflow steps. They're typically used to populate a Feature
Epic. Once created, an implementation issue is usually refined in order to populate and review the
implementation plan and weight.
Example workflow: https://about.gitlab.com/handbook/engineering/development/threat-management/planning/diagram.html#plan-->
## Why are we doing this work
<!--A brief explanation of the why, not the what or how. Assume the reader doesn't know the
background and won't have time to dig-up information from comment threads.-->
`Vulnerability::Read` records are currently created / updated via PostgresQL triggers. This has many downsides.
1. It's difficult to change. Every update requires creating a database migration with raw SQL. There is not even a file that we can edit. e.g. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112233
2. It's invisible when reading or grepping application code.
3. It's difficult to test, since it's decoupled from the application.
4. It's not re-useable.
5. It has less flexibility compared to Ruby and the triggers have grown more complex over time, to the point where they would benefit from being written in a more flexible language.
6. Reviewers aren't accustomed to seeing business logic written in plpgsql, which means that there is a lack of domain expertise among reviewers.
7. We cannot make use of other application-level logic (ex: feature flags).
We should migrate this logic from Postgres over to Rails.
## Relevant links
<!--Information that the developer might need to refer to when implementing the issue.
- [Design Issue](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>)
- [Design 1](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Design 2](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Similar implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/<id>)-->
- https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112233#note_1299594178
- https://gitlab.com/gitlab-org/gitlab/-/issues/386434#note_1282045681
## Non-functional requirements
<!--Add details for required items and delete others.-->
- [ ] Documentation:
- [ ] Feature flag:
- [ ] Performance:
- [ ] Testing:
## Implementation plan
<!--Steps and the parts of the code that will need to get updated.
The plan can also call-out responsibilities for other team members or teams and
can be split into smaller MRs to simplify the code review process.
e.g.:
- MR 1: Part 1
- [ ] gitlab~3412464 Step 1
- [ ] gitlab~3412464 Step 2
- MR 2: Part 2
- [ ] gitlab~2492649 Step 1
- [ ] gitlab~2492649 Step 2
- MR 3: Part 3
- [ ] gitlab~3412464 Step 1
- [ ] gitlab~3412464 Step 2-->
Considerations:
1. Security report ingestion uses bulk inserts which prevents us from using traditional Rails models. For cases where we need to check values before and after update (ex: Create a vulnerability_read when a vulnerability becomes present on the default branch), we'd need to have the insert return that value and manually check it.
2. Vulnerabilities / Findings can be created from multiple places and we need to ensure that all of them create / update Reads in a consistent manner.
<!--Workflow and other relevant labels
# ~"group::" ~"Category:" gitlab~3207279
Other settings you might want to include when creating the issue.
# /assign @
# /epic &-->
## Verification steps
<!--Add verification steps to help GitLab team members test the implementation. This is particularly useful
during the MR review and the gitlab~11090870 step. You may not know exactly what the
verification steps should be during issue refinement, so you can always come back later to add
them.
1. Check-out the corresponding branch
1. ...
1. Profit!-->
epic