Skip to content

feat(minimetrics): Add internal recursion protection #57791

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

Merged
merged 6 commits into from
Oct 10, 2023

Conversation

mitsuhiko
Copy link
Contributor

@mitsuhiko mitsuhiko commented Oct 9, 2023

This fixes an issue where metrics can feed back into themselves. It was assumed that this has been correctly prevented as the tests did not surface the internal metrics. However the tests were incorrect as the internal recursion did not go to the test's backend, but the default metrics backend.

For the regular minimetrics backend the fixture was thus changed to patch the global backend:

@pytest.fixture(scope="function")
def backend():
    rv = MiniMetricsMetricsBackend(prefix="sentrytest.")
    with mock.patch("sentry.utils.metrics.backend", new=rv):
        yield rv

I temporarily considered moving metrics_noop to sentry itself, but the protection still makes sense in the main SDK. Reason being that the transport is in parts (via client) invoked from the flush logic and this can create metrics cycles even without the sentry specific metrics system.

Refs INC-528

@mitsuhiko mitsuhiko requested a review from iambriccardo October 9, 2023 19:51
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Oct 9, 2023
Copy link
Contributor Author

@mitsuhiko mitsuhiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing any of the two fixes, will now make the tests fail reliably.

@wraps(real_add)
def tracked_add(self, ty, *args, **kwargs):
real_add(self, ty, *args, **kwargs)
@metrics_noop
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix #1

@wraps(real_emit)
@metrics_noop
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix #2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note here: i want to add a change to the python SDK which protects the manual flush call. Once that lands, the real_emit must not be marked as metrics_noop or it stops being called. The unittests should catch this though.

@wraps(real_add)
def tracked_add(self, ty, *args, **kwargs):
real_add(self, ty, *args, **kwargs)
@metrics_noop
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would honestly fix it in a different way, since this code works under the assumption that the SDK sets internally the thread local. I would honestly implement it differently and more reliably by just adding the @enter_minimetrics decorator to the monkeypatched functions and protecting the call to the SDK inside of the MinimetricsMetricsBackend.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #57782 but with the finally implementation since it doesn't clear up the state in case of an exception.

Copy link
Contributor Author

@mitsuhiko mitsuhiko Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this code works under the assumption that the SDK sets internally the thread local

I don't believe the code makes such an assumption as metrics_noop sets the thread local.

The risk of a second thread local is this one:

I temporarily considered moving metrics_noop to sentry itself, but the protection still makes sense in the main SDK. Reason being that the transport is in parts (via client) invoked from the flush logic and this can create metrics cycles even without the sentry specific metrics system.

I believe if they do not use the same thread local we have paths where only minimetrics protects itself, but not the outer code path.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok so it sets also the thread local, I thought it was only checking it and not triggering the function.

Copy link
Contributor

@iambriccardo iambriccardo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After my doubts were answered, LGTM

@mitsuhiko mitsuhiko merged commit 286cc85 into master Oct 10, 2023
@mitsuhiko mitsuhiko deleted the feature/internal-protection branch October 10, 2023 08:41
@github-actions github-actions bot locked and limited conversation to collaborators Oct 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants