Skip to content
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

Fix ActionView tracing contexts when span limit is reached #447

Merged
merged 3 commits into from
Jun 11, 2018

Conversation

delner
Copy link
Contributor

@delner delner commented Jun 7, 2018

As revealed by #445, when the hard-cap span limit is reached for a trace (~100K), the Rails renderer tracing starts mishandling spans. This is because it was relying on tracer.call_context.current_span which returns the last span in the context when the context fills, as opposed to the most recently instantiated span.

Although addressing the behavior of tracer.call_context.current_span and its equivalent tracer.active_span remains an issue at large, we can avoid this altogether in the Rails code.

This pull request removes "tracing context" management and context-specific code from the Rails patch altogether, and opts for a much simpler tracer.trace approach instead. This should resolve #445 by simply avoiding the code path in question altogether.

@delner delner added bug Involves a bug integrations Involves tracing integrations dev/refactor Involves refactoring existing components labels Jun 7, 2018
@delner delner added this to the 0.12.1 milestone Jun 7, 2018
@delner delner self-assigned this Jun 7, 2018
@delner delner requested a review from pawelchcki June 7, 2018 20:12
@onnimonni
Copy link

onnimonni commented Jun 8, 2018

Hey @delner I work with @jvalanen and I can confirm that the slow loading page in question works correctly after using this branch. No more undefined method `span_id' for nil:NilClass error.

We are eagerly waiting for the next release 👍

Copy link
Contributor

@pawelchcki pawelchcki left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @delner. The new approach looks much cleaner compared to old.

Just left a few remarks about possible some small resource use gains and about extracting some of the code to a module to avoid having to add exceptions to rubocop.

@@ -24,25 +24,19 @@ def patch_renderer
end

def patch_template_renderer(klass)
# rubocop:disable Metrics/BlockLength
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we consider increasing this limit? So that adding exceptions will not be necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm okay with that. To me, this metric seems a bit arbitrary/silly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright I tried this, but it creates a bunch of other Rubocop problems in other files unrelated to this PR. I don't think we should do this right now; we have a task in the pipeline to cover Rubocop refactoring, so we should do this then.

@@ -24,25 +24,19 @@ def patch_renderer
end

def patch_template_renderer(klass)
# rubocop:disable Metrics/BlockLength
do_once(:patch_template_renderer) do
klass.class_eval do
def render_with_datadog(*args, &block)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can methods defined here be extracted to a module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In theory, they could. I didn't want to do that here because doing so would depend on prepend, we'd end up with version differences between 1.9.3 and 2.0.0+, and then everything would get more complicated, which would increase the risk of creating more bugs. We had just experienced exactly this when we refactored the ActionController patch into a module.

I would like to do this, but we should do it in a separate PR, because its more important we have a stable, verified fix to ship first. Then when that's out, we can clean up the implementation to make things look cleaner.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would moving only def render_with_datadog to a separate module still require 'prepend'?
Ok lets handle that in separate PR.

@@ -90,30 +99,15 @@ def patch_partial_renderer(klass)
do_once(:patch_partial_renderer) do
klass.class_eval do
Copy link
Contributor

Choose a reason for hiding this comment

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

Could methods defined here be extracted to a module as well ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same feedback as above.

attr_accessor :active_datadog_span

def datadog_tracer
Datadog.configuration[:rails][:tracer]
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(@template.try('identifier'))
tracing_contexts[current_span_id][:template_name] = template_name
active_datadog_span.set_tag('rails.template_name', template_name) if template_name
Copy link
Contributor

Choose a reason for hiding this comment

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

'rails.template_name'.freeze this will avoid allocating (and then GCing) the same string multiple times within single request.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know, I tried doing exactly this, after I saw your PR. It's actually not possible to add a constant to the module we're patching here, because it raises a dynamic constant error. The only way I could think around this is either 1) refactoring this into a module (preferable, but must defer per feedback above), or 2) defining the constant in another namespace then referencing that.

The 2nd option I could do now, it just won't be as clean.

@@ -60,8 +54,8 @@ def render_template_with_datadog(*args)
else
layout_name.try(:[], 'virtual_path')
end
@tracing_context[:template_name] = template_name
@tracing_context[:layout] = layout
active_datadog_span.set_tag('rails.template_name', template_name) if template_name
Copy link
Contributor

Choose a reason for hiding this comment

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

freezeing this string might also help a bit with memory use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above.

@tracing_context[:template_name] = template_name
@tracing_context[:layout] = layout
active_datadog_span.set_tag('rails.template_name', template_name) if template_name
active_datadog_span.set_tag('rails.layout', layout) if layout
Copy link
Contributor

Choose a reason for hiding this comment

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

freezeing this string might also help a bit with memory use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above.

@delner
Copy link
Contributor Author

delner commented Jun 8, 2018

@pawelchcki Addressed feeback. Let me know what you think.

@delner delner merged commit b27cc3f into master Jun 11, 2018
@delner delner deleted the bugfix/action_view_tracing_contexts_at_limit branch June 11, 2018 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Involves a bug dev/refactor Involves refactoring existing components integrations Involves tracing integrations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Undefined method `span_id' for nil:NilClass with long requests
3 participants