-
-
Notifications
You must be signed in to change notification settings - Fork 45
Add perf monitoring trace for form loading time #3333
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
Conversation
📝 WalkthroughWalkthrough
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant FLT as FormLoaderTask
participant CC as CCPerfMonitoring
participant FP as FirebasePerf Trace
User->>FLT: Trigger form load
note over FLT: doTaskBackground()
FLT->>CC: startTracing(FORM_LOADING_TIME)
CC-->>FLT: Trace (FP) or null
alt Trace available
FLT->>FP: putAttribute(ATTR_FORM_NAME, formName)
FLT->>CC: stopTracing(FP)
else No trace
note over FLT: Continue without perf trace
end
FLT-->>User: Form loaded (controller ready)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (1 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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. Comment |
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/org/commcare/tasks/FormLoaderTask.java (1)
100-154: Always stop the trace via finally; null-safe form name; keep naming consistent.Guarantees trace closure on all paths (including exceptions), avoids dangling traces, and handles potential null form names. Also updates constant name per naming convention.
- Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.FORM_LOADING_TIME); - FormDef fd = null; + Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FORM_LOADING_TIME); + FormDef fd = null; + try { FormDefRecord formDefRecord = FormDefRecord.getFormDef( CommCareApplication.instance().getAppStorage(FormDefRecord.class), formDefId[0]); File formXml = new File(formDefRecord.getFilePath()); String formHash = FileUtil.getMd5Hash(formXml); File formBin = getCachedForm(formHash); if (formBin.exists()) { // if we have binary, deserialize binary Log.i(TAG, "Attempting to load " + formXml.getName() + " from cached file: " + formBin.getAbsolutePath()); fd = deserializeFormDef((Context)activity, formBin); if (fd == null) { Logger.log(LogTypes.TYPE_RESOURCES, "Deserialization of " + formXml.getName() + " form failed."); // Remove the file, and make a new .formdef from xml formBin.delete(); } } // If we couldn't find a cached version, load the form from the XML if (fd == null) { fd = loadFormFromFile(formXml); } // Try to write the form definition to a cached location try { serializeFormDef(fd, formDefRecord.getFilePath()); } catch (Exception e) { // The cache is a bonus, so if we can't write it, don't crash, but log // it so we can clean up whatever is preventing the cached version from // working Logger.log(LogTypes.TYPE_RESOURCES, "XForm could not be serialized. Error trace:\n" + ForceCloseLogger.getStackTrace(e)); } FormEntryController fec = initFormDef(fd); // Remove previous forms ReferenceManager.instance().clearSession(); setupFormMedia(formDefRecord.getMediaPath()); AndroidFormController formController = new AndroidFormController(fec, mReadOnly, savedFormSession); data = new FECWrapper(formController); - - if (trace != null) { - trace.putAttribute(CCPerfMonitoring.ATTR_FORM_NAME, fd.getName()); - CCPerfMonitoring.INSTANCE.stopTracing(trace); - } - return data; + if (trace != null) { + String formName = (fd != null && fd.getName() != null) ? fd.getName() : formXml.getName(); + trace.putAttribute(CCPerfMonitoring.ATTR_FORM_NAME, formName); + } + return data; + } finally { + CCPerfMonitoring.INSTANCE.stopTracing(trace); + }
🧹 Nitpick comments (1)
app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt (1)
12-12: Keep trace constant naming consistent with existing TRACE_ prefix.*Rename to align with
TRACE_SYNC_ENTITY_LIST_LOADING.- const val FORM_LOADING_TIME = "form_loading_time" + const val TRACE_FORM_LOADING_TIME = "form_loading_time"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt(1 hunks)app/src/org/commcare/tasks/FormLoaderTask.java(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/org/commcare/tasks/FormLoaderTask.java (2)
app/src/org/commcare/CommCareApplication.java (1)
CommCareApplication(157-1279)app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt (2)
startTracing(18-31)stopTracing(33-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Lint Code Base
🔇 Additional comments (4)
app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt (1)
16-16: Ensure no more than 5 custom attributes per Firebase Trace
You’re now at the 5-attribute cap (startTracing() sets four, plus ATTR_FORM_NAME). Verify no other code adds attributes to this trace.app/src/org/commcare/tasks/FormLoaderTask.java (3)
6-7: LGTM: Needed import for Trace.
15-15: LGTM: CCPerfMonitoring import.
150-153: Edge-of-limit on attributes.This adds the 5th attribute to the trace. Please ensure no other code adds more attributes to this same trace name.
fc29f4f to
1e3d2ee
Compare
04899ff to
ddf5988
Compare
|
|
||
| data = new FECWrapper(formController); | ||
|
|
||
| if (trace != null) { |
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.
- can we put this in a try-catch all exception block, as this should not affect functionality in case of bugs like NPEs
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.
@shubham1g5 Can FormDef be null at this point? There is a try-catch block inside stopTracing()
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.
I don't think it can be but just suggested it from more of a best practice perspective, happy for the PR to be merged without that as well
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.
My question was more in the sense that we probably want to "cancel" the tracing if the FormDef is null, because otherwise it will mean uploading incomplete data. I've opted for Null checking FormDef
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.
@avazirna I probably didn't clearify my point completely but the best practice I was considering here is to wrap the non-essential code in a catch all exception block (like we do inReportingUtils ) instead of null checks or a particular edge case speifically. Think the null checks are worse here as they give an impression that they can be null which they can't be.
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.
@shubham1g5 yeah, I get you, and it makes sense. I'm a bit conservative when it comes to try-catches like that, but let me add that.
8fb0e5f to
a7c5b25
Compare
a7c5b25 to
0dabb6e
Compare
| attrs.put(CCPerfMonitoring.ATTR_FORM_NAME, fd.getName()); | ||
| attrs.put(CCPerfMonitoring.ATTR_FORM_XMLNS, fd.getMainInstance().schema); | ||
| CCPerfMonitoring.INSTANCE.stopTracing(trace, attrs); | ||
| } catch (Exception ignored) {} |
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.
think we would still want to log the exception atleast
Product Description
This PR implements a performance custom trace to measure form loading times. This is part of an effort to collect performance data related key events in CommCare.
Ticket: https://dimagi.atlassian.net/browse/SAAS-18091
Safety Assurance
Safety story
This is supposed to be a lightweight, null-safe operation that has no impact on the user experience.
Labels and Review