-
-
Notifications
You must be signed in to change notification settings - Fork 218
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
Report app dependency addition/removal to DataDog #35455
Conversation
corehq/apps/app_manager/models.py
Outdated
|
||
latest_build = get_latest_build_doc(self.domain, self.id) | ||
if not latest_build: |
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.
When would there not be a latest build?
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.
The "latest_build" here refers to the previous build, as the new build has not been made at this point. So the "latest_build" could be None
if it's the first build.
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.
Ah I see, thanks for the clarification
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.
nit: to make it slightly better, you could consider naming this last_build.
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.
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.
LGTM
return | ||
|
||
if has_dependencies(latest_build) and not has_dependencies(new_build): | ||
metrics_counter('commcare.app_build.dependencies_removed') |
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 like that we have simplified the approach here.
Wanted to check if this metric in itself would be useful? Don't we need the uptake as well?
I believe on datadog you can have a counter with both positive and negative values, so you get a graph with both additions and removals at once.
example here
Or we could report uptake separately, which seems easy enough to add now.
if (not latest_build or not has_dependencies(latest_build)) and has_dependencies(new_build):
metrics_counter('commcare.app_build.dependencies_added')
I would vote for the single counter with both +/-
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 believe on datadog you can have a counter with both positive and negative values, so you get a graph with both additions and removals at once.
I'll check this out. Thanks for the suggestion.
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.
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.
So, these will show as two separate metrics right? not as one? or they can be combined if needed on the datadog UI?
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.
@mkangia They can be combined as needed on Data Dog.
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.
Great 👍
corehq/apps/app_manager/models.py
Outdated
return copy | ||
|
||
def check_build_dependencies(self, new_build): | ||
""" | ||
Reports whether the app dependencies have been removed. |
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.
looks like the documentation needs to be updated.
And possibly the function name 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.
Good catch
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.
corehq/apps/app_manager/models.py
Outdated
""" | ||
|
||
def has_dependencies(build): | ||
if isinstance(build, Application): |
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.
wont it always be Application object since we wrapped it?
Its unclear why we are checking this here.
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.
The new_build
is wrapped and will be an instance of Application
, but the last_build
is not (see here). I suppose I have two options:
- Also make sure to wrap the
last_build
results and remove the instance checking - Leave as is and handle different instance types.
Any preference from your side?
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.
Actually, no, I'm going to wrap it as well, because I think the fact that you had to ask this question makes the code less obvious as it is now.
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.
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.
Looking good 👍
corehq/apps/app_manager/models.py
Outdated
) | ||
|
||
new_build_has_dependencies = has_dependencies(new_build) | ||
|
||
last_build = get_latest_build_doc(self.domain, self.id) | ||
last_build = ApplicationBase.wrap(last_build) if last_build else None |
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.
nit: Not sure the difference but should this be cls.wrap
? just like when the copy is created.
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.
This is in instance method, so either self.__class__.wrap
or ApplicationBase
- I prefer the latter.
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.
Will that impact if the function is called by inherited classes instead?
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.
Yes, if a subclass calls this method that would mean the ApplicationBase wrap method is still called and not the subclass's wrap
method. I think this is OK, since we don't use anything except the profile attribute. But that is a good point, maybe it should take the subclass's own implementation of wrap
?
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.
ApplicationBase wrap method is still called and not the subclass's wrap method
Ya. We would be wrapping data for a different class on ApplicationBase which isn't ideal and would further lead to issues if the subclass adds more properties than ApplicationBase, which will get skipped leading to loss of data in case a save is performed.
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.
Good point. I'll update
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.
You might check out wrap_app for this.
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.
@orangejenny What is the exact benefit of using wrap_app(last_build, self.__class__)
instead of simply self.__class__.wrap(last_build)
? I see the wrap_doc
function have the get_correct_app_class
addition, but that's only coming into play if I don't pass in the class name, which I will do in this instance.
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.
Probably doesn't matter, then. I had just read the original code when making the suggestion.
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.
wrap_app
looks like a nice catch all for different application and similar docs with a hard failure in case of unexpected scenarios. Definitely good to use when dealing with more complex code.
The class wrap seems safe here since we are passing a build explicitly.
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.
Looks good to me.
Can we add some tests?
I can add some. Didn't think it was necessary since it's an easy thing to test locally, but for peace of mind I'll add some. |
…commcare-hq into cs/SC-4053-add-metric-to-datadog
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.
Thank you @Charl1996
Are we to release this after populating the datadog dashboard? so you will merge this only after it?
💯 |
Relates to this PR
Technical Summary
Ticket
This PR increments a counter on DataDog whenever the app dependencies feature has been disabled for a build which had it enabled which allows us to track the feature drop-out rate. We don't need to know any domain information, just the overall counter.
Note that the counter is incremented during the
make_build
phase.(The related PR will be changed to support a management command which will be run to "top up" the counter with historic data)
Safety Assurance
Safety story
Tested locally as far as I could. Tested on staging as well.
Automated test coverage
No automated tests included.
QA Plan
No formal QA will be done
Rollback instructions
Labels & Review