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

feat(sessions): Implement automatic session tracking #1715

Merged
merged 40 commits into from
Mar 7, 2022
Merged

Conversation

sl0thentr0py
Copy link
Member

@sl0thentr0py sl0thentr0py commented Feb 9, 2022

This adds automatic session tracking to our rack middleware (and hence rails).
The key components here are a SessionFlusher that spawns a new thread that sends pending aggregate sessions once a minute and a simple Session class for recording those individual sessions.

Note that we currently only implement request mode sessions which are aggregated in the SDK as compared to application mode sessions which are sent as soon as they finish. The main reason for this is the scale of most servers out there which would overload our ingestion pipelines.

The Session class could potentially be extended in the future for application mode sessions but for now it doesn't add much user value.

There are a couple of TODOs left which are slightly orthogonal to the main feature so I will make separate PRs for those.

closes #1711

References


Implementation progress

  • Session flusher class, spawn new thread
  • Session class
  • Aggregate sessions
  • Hub / client apis
  • Rack middleware hook
  • Rails end-end
  • test coverage

In separate PRs

@codecov-commenter
Copy link

codecov-commenter commented Feb 9, 2022

Codecov Report

Merging #1715 (6ddd1ed) into master (bad64df) will increase coverage by 0.03%.
The diff coverage is 98.80%.

❗ Current head 6ddd1ed differs from pull request most recent head 6e89778. Consider uploading reports for the commit 6e89778 to get more accurate results

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1715      +/-   ##
==========================================
+ Coverage   98.40%   98.43%   +0.03%     
==========================================
  Files         141      144       +3     
  Lines        8079     8312     +233     
==========================================
+ Hits         7950     8182     +232     
- Misses        129      130       +1     
Impacted Files Coverage Δ
sentry-ruby/lib/sentry/rack/capture_exceptions.rb 95.12% <87.50%> (+0.12%) ⬆️
sentry-ruby/lib/sentry/session_flusher.rb 97.77% <97.77%> (ø)
sentry-ruby/lib/sentry-ruby.rb 95.86% <100.00%> (+0.27%) ⬆️
sentry-ruby/lib/sentry/configuration.rb 98.42% <100.00%> (+0.01%) ⬆️
sentry-ruby/lib/sentry/hub.rb 100.00% <100.00%> (ø)
sentry-ruby/lib/sentry/scope.rb 100.00% <100.00%> (ø)
sentry-ruby/lib/sentry/session.rb 100.00% <100.00%> (ø)
sentry-ruby/lib/sentry/transport.rb 98.88% <100.00%> (+0.01%) ⬆️
...entry-ruby/lib/sentry/transport/dummy_transport.rb 100.00% <100.00%> (ø)
sentry-ruby/spec/sentry/configuration_spec.rb 100.00% <100.00%> (ø)
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bad64df...6e89778. Read the comment docs.

@sl0thentr0py sl0thentr0py marked this pull request as ready for review February 18, 2022 14:35
@sl0thentr0py sl0thentr0py changed the title Sessions WIP feat(sessions): Implement automatic session tracking Feb 18, 2022
Base automatically changed from ref-envelope-rate-limit to master February 22, 2022 17:35
@sl0thentr0py
Copy link
Member Author

sl0thentr0py commented Feb 28, 2022

So I'm gonna document my testing here one by one.
The setup is as follows:
I'm using this branch with a simple logging of the total thread counts and the session flusher thread count every 10 seconds and on init and exit. And the rails app I'm using is here.

First run, normal behavior with some success and error routes. We see that during normal behavior, there is one flusher thread and it is killed successfully before exiting.

Thread Count log
INIT STATE
--------------------
total count: 1
flusher count: 0
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 1
--------------------
EXIT STATE
--------------------
total count: 9
flusher count: 0
--------------------
Proof that end-end flow worked image

sentry-ruby/lib/sentry/session.rb Outdated Show resolved Hide resolved
@antonpirker
Copy link
Member

antonpirker commented Mar 1, 2022

My review is just very "on the surface", because I am not an Ruby export. But everything looks clean (and done the same way as in Python)

@sl0thentr0py
Copy link
Member Author

thx for taking a look!

@sl0thentr0py
Copy link
Member Author

sl0thentr0py commented Mar 1, 2022

Next test was to test the failure resiliency of the flusher. Basically checking two things

  • if there's an exception inside the thread, the ensure_thread ensures that a new thread respawns eventually
  • there is only a single flusher thread at a time, other threads exit/don't leak and we don't add more threads randomly

This was done with this commit to throw a random exception with 25% probability.

Thread exception in rails log
#<Thread:0x00007f91d0123ce8@/Users/neel/sentry/sdks/sentry-ruby/sentry-ruby/lib/sentry/session_flusher.rb:77 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
	3: from /Users/neel/sentry/sdks/sentry-ruby/sentry-ruby/lib/sentry/session_flusher.rb:78:in `block in ensure_thread'
	2: from /Users/neel/sentry/sdks/sentry-ruby/sentry-ruby/lib/sentry/session_flusher.rb:78:in `loop'
	1: from /Users/neel/sentry/sdks/sentry-ruby/sentry-ruby/lib/sentry/session_flusher.rb:80:in `block (2 levels) in ensure_thread'
/Users/neel/sentry/sdks/sentry-ruby/sentry-ruby/lib/sentry/session_flusher.rb:21:in `flush': Oops in thread (RuntimeError)

The thread count log shows 0 for the flusher thread count when that exception happens and the thread crashes. A new one is eventually respawned on the the next add_session call. We see that the thread count will always be 0/1 and we will not add more threads in any way if there is already an existing thread.

Thread Count log
INIT STATE
--------------------
total count: 1
flusher count: 0
--------------------
--------------------
total count: 26
flusher count: 1
--------------------
--------------------
total count: 26
flusher count: 1
--------------------
--------------------
total count: 25
flusher count: 0   # <---- crash happened before this log
--------------------
--------------------
total count: 25
flusher count: 0
--------------------
--------------------
total count: 26
flusher count: 1  # <----- thread respawned here once I visited some pages
--------------------
--------------------
total count: 26
flusher count: 1
--------------------
--------------------
total count: 26
flusher count: 1
--------------------
EXIT STATE
--------------------
total count: 9
flusher count: 0
--------------------

@st0012 st0012 added this to the 5.2.0 milestone Mar 1, 2022
Copy link
Collaborator

@st0012 st0012 left a comment

Choose a reason for hiding this comment

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

I finally got time to do a more thorough review, sorry for the waiting.
The overall feature still looks good 👍 I only added some nitpick-ish naming/testing suggestions 🙂

sentry-ruby/lib/sentry/session.rb Show resolved Hide resolved
sentry-ruby/lib/sentry/session.rb Outdated Show resolved Hide resolved
sentry-ruby/lib/sentry/session.rb Outdated Show resolved Hide resolved
sentry-ruby/lib/sentry/session_flusher.rb Outdated Show resolved Hide resolved
sentry-ruby/lib/sentry/session_flusher.rb Outdated Show resolved Hide resolved
sentry-ruby/lib/sentry/transport.rb Show resolved Hide resolved
sentry-ruby/spec/sentry/session_flusher_spec.rb Outdated Show resolved Hide resolved
sentry-ruby/spec/sentry/session_flusher_spec.rb Outdated Show resolved Hide resolved
@sl0thentr0py
Copy link
Member Author

@st0012 made some updates and left comments for the rest

@st0012
Copy link
Collaborator

st0012 commented Mar 7, 2022

@sl0thentr0py since we're now close to completion, can you also add a changelog entry for the feature? preferably also include a simple description, related configs & Sentry doc, and a screenshot? Example: https://github.com/getsentry/sentry-ruby/releases/tag/4.8.0

@sl0thentr0py
Copy link
Member Author

@st0012 done!

CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Collaborator

@st0012 st0012 left a comment

Choose a reason for hiding this comment

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

🎉

@st0012 st0012 merged commit e727167 into master Mar 7, 2022
5.x automation moved this from In progress to Done Mar 7, 2022
@st0012 st0012 deleted the sessions branch March 7, 2022 15:20
@@ -189,11 +194,18 @@ def init(&block)
@main_hub = hub
@background_worker = Sentry::BackgroundWorker.new(config)

@session_flusher = if config.auto_session_tracking
Sentry::SessionFlusher.new(config, client)
Copy link
Collaborator

Choose a reason for hiding this comment

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

@sl0thentr0py I think we should also check if the SDK is deactivated before initializing the flusher, especially to avoid the additional thread that won't be used. We can use Configuration#enabled_in_current_env? for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
5.x
Done
Development

Successfully merging this pull request may close these issues.

Session tracking/release health
4 participants