Skip to content

Conversation

@bmansoob
Copy link
Contributor

@bmansoob bmansoob commented Feb 6, 2025

  • Replaces CurrentAttributes with local thread storage which gets reset by the middleware at the end. The reason for this is if the profiling middleware runs before this, then the value would be inconsistent if it is read again on the way out (sending response back). I have tried to maintain the same API interface so that it does not break existing clients
  • We now persist profile_id in metadata, so that the clients do not have to do this.

@bmansoob bmansoob force-pushed the profile-id-improvements branch from 4fb0cf6 to 0392954 Compare February 6, 2025 16:58
@bmansoob
Copy link
Contributor Author

bmansoob commented Feb 6, 2025

once approved -- i would like to try this branch first in production before cutting a new release

# a vernier "result" object for vernier
def initialize(data, id: nil, context: nil)
@id = id.presence || ProfileId.current
ProfileId.set(id) if id.present?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We were not doing this before. If someone provides an Id we should set ProfileId too so that the values are consistent.

Copy link
Member

@manuelfelipe manuelfelipe left a comment

Choose a reason for hiding this comment

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

We now persist profile_id in metadata, so that the clients do not have to do this.

nice. Thanks for that!

@manuelfelipe
Copy link
Member

We now persist profile_id in metadata, so that the clients do not have to do this.

Now that looked at the corresponding prod change, maybe we should also add the profiler key to the metadata from here similarly to how you are doing for profile_id instead of having all apps setting that value

@bmansoob bmansoob force-pushed the profile-id-improvements branch from 96e054d to 785570c Compare February 6, 2025 17:53
@bmansoob bmansoob force-pushed the profile-id-improvements branch from 785570c to c8595fb Compare February 6, 2025 20:23
def initialize(data, id: nil, context: nil)
@id = id.presence || ProfileId.current
ProfileId.set(id) if id.present?
@id = ProfileId.current
Copy link
Member

Choose a reason for hiding this comment

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

IMO You don't need to track this as an ivar anymore. Any call should use the current value. You can only have 1 profile per request, right?

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 a good point 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed this to:

def id
      metadata[PROFILE_ID_METADATA_KEY]
 end


module AppProfiler
PROFILE_ID_METADATA_KEY = :profile_id
PROFILE_BACKEND_METADATA_KEY = :profiler
Copy link
Member

Choose a reason for hiding this comment

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

I would keep these in BaseProfile. Putting them in two places doesn't really make sense if they're solely used there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Clients can use these ids when dealing with profile metadata. I think accessing it as AppProfiler:: PROFILE_ID_METADATA_KEY is better than nesting it under BaseProfile. I have removed it from BaseProfile.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, as long as it isn't repeated I'm happy. 👍

private_constant :INTERNAL_METADATA_KEYS
class UnsafeFilename < StandardError; end
PROFILE_ID_METADATA_KEY = :profile_id
PROFILE_BACKEND_METADATA_KEY = :profiler
Copy link
Member

Choose a reason for hiding this comment

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

Nit: I'd consider adding these to private_const above, but not strictly necessary if you need them in tests. You could just use the plain value there instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

@bmansoob
Copy link
Contributor Author

bmansoob commented Feb 7, 2025

@gmcgibbon how bad would it be if I change this to

AppProfiler.profile_file_name.call(metadata) + format
to take the profile object, instead of metadata?

This is quite recent, like a week or so ago -- so not sure if there are usages out in the wild 😬

Will allow us to just use the profile object and its methods (id is now deeply integrated). I kind of dread i did not do this to start with.

Copy link
Member

@gmcgibbon gmcgibbon left a comment

Choose a reason for hiding this comment

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

LGTM after these changes


class << self
def backend_name
NAME # cannot reference Backend::VernierBackend because of different ruby versions we have to support
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
NAME # cannot reference Backend::VernierBackend because of different ruby versions we have to support
BACKEND_NAME

Are you able to make Backend::VernierBackend.name and other classes reference this constant so we're not repeating ourselves? We need to eventually add loading tests to make sure we don't regress #155.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done - think I did all of them

 > Rg "AppProfiler::Backend::VernierBackend.name" .

# returns nothing
 > Rg "AppProfiler::VernierProfile::BACKEND_NAME" .


./test/app_profiler/run_test.rb
35:      AppProfiler.run(backend: AppProfiler::VernierProfile::BACKEND_NAME) do
36:        assert_equal(AppProfiler::VernierProfile::BACKEND_NAME, AppProfiler.backend)

./lib/app_profiler.rb
167:          backend_name&.to_sym == AppProfiler::VernierProfile::BACKEND_NAME
181:      RUBY_VERSION >= "3.2.1" && defined?(AppProfiler::VernierProfile::BACKEND_NAME)

./lib/app_profiler/request_parameters.rb
36:      if AppProfiler.vernier_supported? && backend == AppProfiler::VernierProfile::BACKEND_NAME &&

./test/app_profiler/backend_test.rb
12:      assert_raises(BackendError) { AppProfiler.backend = AppProfiler::VernierProfile::BACKEND_NAME }
24:      assert(AppProfiler.backend = AppProfiler::VernierProfile::BACKEND_NAME)
25:      assert_equal(AppProfiler.backend, AppProfiler::VernierProfile::BACKEND_NAME)
52:        AppProfiler.backend_for(AppProfiler::VernierProfile::BACKEND_NAME),

middleware.call(mock_request_env(path: "/?profile=cpu"))
end
end
assert_nil(Thread.current[ProfileId::PROFILE_ID_KEY])
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assert_nil(Thread.current[ProfileId::PROFILE_ID_KEY])
assert_nil(ProfileId.current)

If you want to test threads, you can do so in a ProfileId test. Now that we aren't using current attributes we should test this more thoroughly anyway.

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 will think of adding more meaningful threads in ProfileId tests. But. given what we found for current attributes regarding order of middlewars, I specifically wanted to test this after middleware has run and finished. I ack this feels a bit out of of place - but is kind of of imp. Alternatively, I can expose a method nil? which is used only for tests and is without any side-effects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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


module AppProfiler
PROFILE_ID_METADATA_KEY = :profile_id
PROFILE_BACKEND_METADATA_KEY = :profiler
Copy link
Member

Choose a reason for hiding this comment

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

Ok, as long as it isn't repeated I'm happy. 👍

@gmcgibbon
Copy link
Member

@bmansoob You can make that change, but because it is a released feature you need to think about deprecation cycling the old behaviour. I would wrap a proc on assignment that checks if the type of the input. What you could do is:

  • Make a separate PR with the change to behaviour + docs + changelog
  • Make BaseProfile delegate [] to metadata so legacy procs would still work
  • Depending on how you feel about having the subscripting operator work that way, raise a deprecation warning, or keep the behaviour

@bmansoob
Copy link
Contributor Author

bmansoob commented Feb 7, 2025

@bmansoob You can make that change, but because it is a released feature you need to think about deprecation cycling the old behaviour. I would wrap a proc on assignment that checks if the type of the input. What you could do is:

thanks -- something to ponder and do in a separate PR 😅

@bmansoob
Copy link
Contributor Author

bmansoob commented Feb 7, 2025

I will squash the commits in a bit and test this branch in production before cutting a new release.

@bmansoob bmansoob force-pushed the profile-id-improvements branch from 1694438 to 2a49a66 Compare February 10, 2025 13:50
@bmansoob
Copy link
Contributor Author

squashed

@bmansoob
Copy link
Contributor Author

direction change - going to merge and release the gem.

@bmansoob bmansoob merged commit bf75b42 into main Feb 10, 2025
7 checks passed
@bmansoob bmansoob deleted the profile-id-improvements branch February 10, 2025 17:29
@bmansoob bmansoob mentioned this pull request Feb 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants