-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Metrics for block-level sync #1877
Changes from 17 commits
ba79439
2818a3e
336c17a
8c0280f
d03061e
aeff6a1
62fa93b
a5ef23f
66fa44e
8e82202
1eeec2f
cfb41c3
7a5814a
a567abb
e1c1d95
b5a6b1b
e67a5c9
bb928d3
9a44264
d79dfbb
4126890
e237095
f94d1e6
1cf16d4
50b041b
0b2be88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,10 +11,14 @@ def __sync_from_user__(params) | |
end | ||
|
||
def __sync_to_user__(response_key) | ||
AuditLog.create_logs_async(current_user, records_to_sync, "fetch", Time.current) unless disable_audit_logs? | ||
records = records_to_sync | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we put proper ivar caching into the methods to prevent this kind of inefficiency in future? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏼 #1921 is doing a bunch of nice memoization, I think we can do this there. I'll add a task for it in the PR. |
||
|
||
AuditLog.create_logs_async(current_user, records, "fetch", Time.current) unless disable_audit_logs? | ||
log_block_level_sync_metrics(response_key) | ||
|
||
render( | ||
json: { | ||
response_key => records_to_sync.map { |record| transform_to_response(record) }, | ||
response_key => records.map { |record| transform_to_response(record) }, | ||
"process_token" => encode_process_token(response_process_token) | ||
}, | ||
status: :ok | ||
|
@@ -35,6 +39,27 @@ def disable_audit_logs? | |
false | ||
end | ||
|
||
def log_block_level_sync_metrics(response_key) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Centralize all the block-sync logging in one place so that the loggers don't end up being called multiple times during the code-path and are easy to find in one place. |
||
Rails.logger.tagged("Block Sync") do | ||
if resync_token_modified? | ||
Rails.logger.info msg: "Resync token modified", resource: response_key | ||
end | ||
|
||
if current_user | ||
if sync_region_modified? | ||
Rails.logger.info msg: "Sync region ID modified", | ||
region_type: current_sync_region.class.name, | ||
region_id: current_sync_region.id, | ||
resource: response_key | ||
end | ||
|
||
if block_level_sync? | ||
Rails.logger.info msg: "The current_sync_region set to block", block_id: current_block.id | ||
end | ||
end | ||
end | ||
end | ||
|
||
def params_with_errors(params, errors) | ||
error_ids = errors.map { |error| error[:id] } | ||
params | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
class ReportDuplicatePassports | ||
include Memery | ||
|
||
IDENTIFIER_TYPES = %w[simple_bp_passport] | ||
delegate :logger, to: Rails | ||
|
||
def self.report | ||
new.report | ||
end | ||
|
||
def report | ||
logger.tagged("ReportDuplicatePassports") do | ||
logger.info msg: "#{duplicate_passports_count} passports have duplicate patients across facilities" | ||
end | ||
|
||
Statsd.instance.gauge("ReportDuplicatePassports.size", duplicate_passports_count) | ||
end | ||
|
||
memoize def duplicate_passports_count | ||
passports_at_multiple_facilities | ||
.where(identifier: passports_with_multiple_patients) | ||
.where(identifier_type: IDENTIFIER_TYPES) | ||
.pluck(:identifier) | ||
.count | ||
end | ||
|
||
# this is a utility function for fetching the actual duplicate passports | ||
# you could call this and get counts from it but duplicate_passports_count is faster | ||
# leaving this here as useful method for debugging | ||
def duplicate_passports | ||
PatientBusinessIdentifier | ||
.where(identifier_type: IDENTIFIER_TYPES) | ||
.where(identifier: passports_at_multiple_facilities) | ||
.where(identifier: passports_with_multiple_patients) | ||
end | ||
|
||
def passports_at_multiple_facilities | ||
PatientBusinessIdentifier | ||
.group(:identifier) | ||
.having("COUNT(DISTINCT #{passport_assigning_facility}) > 1") | ||
.select(:identifier) | ||
end | ||
|
||
def passports_with_multiple_patients | ||
PatientBusinessIdentifier | ||
.group(:identifier) | ||
.having("COUNT(DISTINCT patient_id) > 1") | ||
.select(:identifier) | ||
end | ||
|
||
private | ||
|
||
def passport_assigning_facility | ||
"COALESCE((metadata->'assigning_facility_id'), (metadata->'assigningFacilityUuid'))::text" | ||
end | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does the output of this report generally look like? Can we post some logs from a test run for good measure? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me fetch some logs and post a counter... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This happens to be the actual number as it stands on production... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
require "rails_helper" | ||
|
||
RSpec.describe ReportDuplicatePassports do | ||
describe ".duplicate_passports" do | ||
context "for passports with the same identifiers" do | ||
let!(:identifier) { SecureRandom.uuid } | ||
|
||
it "does not have passports with same patient at the same facility" do | ||
facility_id = SecureRandom.uuid | ||
patient = create(:patient, business_identifiers: []) | ||
create_list(:patient_business_identifier, 2, identifier: identifier, patient: patient, metadata: {assigningFacilityUuid: facility_id}) | ||
|
||
expect(described_class.new.duplicate_passports).to be_empty | ||
end | ||
|
||
it "does not have passports with duplicate patients (different by id) at the same facility" do | ||
facility_id = SecureRandom.uuid | ||
patients = create_list(:patient, 2, business_identifiers: []) | ||
patients.map do |patient| | ||
create(:patient_business_identifier, identifier: identifier, patient: patient, metadata: {assigningFacilityUuid: facility_id}) | ||
end | ||
|
||
expect(described_class.new.duplicate_passports).to be_empty | ||
end | ||
|
||
it "does not have passports with the same patient assigned to it at different facilities" do | ||
patient = create(:patient, business_identifiers: []) | ||
create(:patient_business_identifier, identifier: identifier, patient: patient, metadata: {assigningFacilityUuid: SecureRandom.uuid}) | ||
create(:patient_business_identifier, identifier: identifier, patient: patient, metadata: {assigningFacilityUuid: SecureRandom.uuid}) | ||
|
||
expect(described_class.new.duplicate_passports).to be_empty | ||
end | ||
|
||
it "only has different patients assigned to a passport at different facilities" do | ||
patients = create_list(:patient, 2, business_identifiers: []) | ||
identifiers = | ||
[create(:patient_business_identifier, identifier: identifier, patient: patients.first, metadata: {assigningFacilityUuid: SecureRandom.uuid}), | ||
create(:patient_business_identifier, identifier: identifier, patient: patients.second, metadata: {assigningFacilityUuid: SecureRandom.uuid})] | ||
|
||
expect(described_class.new.duplicate_passports).to match_array identifiers | ||
expected_count = described_class.new.duplicate_passports.group(:identifier).count.keys.count | ||
expect(described_class.new.duplicate_passports_count).to eq(expected_count) | ||
end | ||
end | ||
|
||
context "for passports without the same identifiers" do | ||
it "does not count them" do | ||
patients = create_list(:patient, 2, business_identifiers: []) | ||
create(:patient_business_identifier, identifier: SecureRandom.uuid, patient: patients.first, metadata: {assigningFacilityUuid: SecureRandom.uuid}) | ||
create(:patient_business_identifier, identifier: SecureRandom.uuid, patient: patients.second, metadata: {assigningFacilityUuid: SecureRandom.uuid}) | ||
|
||
expect(described_class.new.duplicate_passports).to be_empty | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These
time
calls are now timing the old sync code (post revert), but whenever we ship the new-new perf changes, the body can be swapped and the timer can stay.