perfm 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +35 -12
- data/app/models/perfm/sidekiq_gvl_metric.rb +22 -0
- data/lib/generators/perfm/sidekiq_gvl_metrics/sidekiq_gvl_metrics_generator.rb +23 -0
- data/lib/generators/perfm/sidekiq_gvl_metrics/templates/create_perfm_sidekiq_gvl_metrics.rb.erb +17 -0
- data/lib/perfm/agent.rb +6 -1
- data/lib/perfm/configuration.rb +5 -0
- data/lib/perfm/engine.rb +8 -0
- data/lib/perfm/middleware/sidekiq_gvl_instrumentation.rb +35 -0
- data/lib/perfm/sidekiq_gvl_metrics_analyzer.rb +127 -0
- data/lib/perfm/storage/local.rb +5 -1
- data/lib/perfm/version.rb +1 -1
- data/lib/perfm.rb +2 -0
- metadata +16 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c533c4d2398428da1e5d70056f68282566ffb4a6521737b8fa402a133ede71dd
|
4
|
+
data.tar.gz: 50f92a5880b9bd8773ea4f8692f0e40eb531606b1e5b9e38b5cf77d4c3fc2453
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70e239a276d658595cae7ad6f2dd366d1d25faaff2f42e6404688fe860b92eafb9a2c94df2ea74b5424a44f592f52422261de81bc26ab1512553f37a2705b5df
|
7
|
+
data.tar.gz: 2ccdb7cda8634d8d381d36c3ccf798020fa6837c1aaa9e63db749ab334674f28892f85fcf4308fe87aaf9b7446923104f1c54e01d11516f358f91410057cac86
|
data/README.md
CHANGED
@@ -1,16 +1,17 @@
|
|
1
|
-
Perfm
|
2
|
-
|
1
|
+
# Perfm
|
2
|
+
|
3
3
|
Perfm aims to be a performance monitoring tool for Ruby on Rails applications. Currently, it has support for GVL instrumentation and provides analytics to help optimize Puma thread concurrency settings based on the collected GVL data.
|
4
4
|
|
5
|
-
Requirements
|
6
|
-
|
5
|
+
## Requirements
|
6
|
+
|
7
7
|
- Ruby: MRI 3.2+
|
8
8
|
|
9
9
|
This is because the GVL instrumentation API was [added](https://bugs.ruby-lang.org/issues/18339) in 3.2.0. Perfm makes use of the [gvl_timing](https://github.com/jhawthorn/gvl_timing) gem to capture per-thread timings for each GVL state.
|
10
10
|
|
11
|
-
Installation
|
12
|
-
|
11
|
+
## Installation
|
12
|
+
|
13
13
|
Add perfm to your Gemfile.
|
14
|
+
|
14
15
|
```ruby
|
15
16
|
gem 'perfm'
|
16
17
|
```
|
@@ -23,8 +24,8 @@ bin/rails generate perfm:install
|
|
23
24
|
|
24
25
|
This will create a migration file with a table to store the GVL metrics. Run the migration and configure the gem as described below.
|
25
26
|
|
26
|
-
Configuration
|
27
|
-
|
27
|
+
## Configuration
|
28
|
+
|
28
29
|
Configure Perfm in an initializer:
|
29
30
|
|
30
31
|
```ruby
|
@@ -83,7 +84,6 @@ The following features are currently in beta and may have limited functionality
|
|
83
84
|
|
84
85
|
### Perfm queue latency monitor
|
85
86
|
|
86
|
-
|
87
87
|
The queue latency monitor tracks Sidekiq queue times and raises alerts when the queue latency exceed their thresholds. To enable this feature, set `config.monitor_sidekiq_queues = true` in your Perfm configuration.
|
88
88
|
|
89
89
|
ruby
|
@@ -101,9 +101,31 @@ When enabled, Perfm will monitor your Sidekiq queues and raise a `Perfm::Errors:
|
|
101
101
|
|
102
102
|
Perfm expects queues that need latency monitoring to follow this naming pattern:
|
103
103
|
|
104
|
-
-
|
105
|
-
-
|
106
|
-
-
|
104
|
+
- `within_X_seconds` (e.g., within_5_seconds)
|
105
|
+
- `within_X_minutes` (e.g., within_2_minutes)
|
106
|
+
- `within_X_hours` (e.g., within_1_hours)
|
107
|
+
|
108
|
+
### Sidekiq GVL Instrumentation
|
109
|
+
|
110
|
+
To enable GVL instrumentation for Sidekiq, first run the generator to add migrations for the required table to store the metrics.
|
111
|
+
|
112
|
+
```bash
|
113
|
+
bin/rails generate perfm:sidekiq_gvl_metrics
|
114
|
+
```
|
115
|
+
|
116
|
+
Then enable the `monitor_sidekiq_gvl` configuration in your initializer.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Perfm.configure do |config|
|
120
|
+
config.monitor_sidekiq_gvl = true
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
When enabled, Perfm will collect GVL metrics at a job level, similar to how it collects metrics for HTTP requests. This can be used to analyze GVL metrics specifically for Sidekiq queues to understand their I/O characteristics.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
Perfm::SidekiqGvlMetric.calculate_queue_io_percentage("within_5_seconds")
|
128
|
+
```
|
107
129
|
|
108
130
|
### Heap analyzer
|
109
131
|
|
@@ -145,6 +167,7 @@ end
|
|
145
167
|
#### Controller to generate heap dumps
|
146
168
|
|
147
169
|
As we need to invoke rbtrace from the same process, we'll use a controller itself to invoke the `HeapDumper`.
|
170
|
+
|
148
171
|
```ruby
|
149
172
|
class Perfm::Admin::HeapDumpsController < ActionController::Base
|
150
173
|
skip_forgery_protection
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Perfm
|
2
|
+
class SidekiqGvlMetric < ApplicationRecord
|
3
|
+
self.table_name = "perfm_sidekiq_gvl_metrics"
|
4
|
+
|
5
|
+
scope :within_time_range, ->(start_time, end_time) {
|
6
|
+
where(created_at: start_time..end_time)
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.calculate_queue_io_percentage(queue_name, start_time: nil, end_time: nil)
|
10
|
+
scope = where(queue: queue_name)
|
11
|
+
scope = scope.within_time_range(start_time, end_time) if start_time && end_time
|
12
|
+
|
13
|
+
total_run_ms = scope.sum(:run_ms)
|
14
|
+
total_idle_ms = scope.sum(:idle_ms)
|
15
|
+
|
16
|
+
total_time = total_run_ms + total_idle_ms
|
17
|
+
return 0.0 if total_time == 0
|
18
|
+
|
19
|
+
((total_idle_ms.to_f / total_time) * 100.0).round(2)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module Perfm
|
6
|
+
class SidekiqGvlMetricsGenerator < Rails::Generators::Base
|
7
|
+
include ActiveRecord::Generators::Migration
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
def create_migration_file
|
11
|
+
migration_template(
|
12
|
+
'create_perfm_sidekiq_gvl_metrics.rb.erb',
|
13
|
+
File.join(db_migrate_path, "create_perfm_sidekiq_gvl_metrics.rb")
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def migration_version
|
20
|
+
"[#{ActiveRecord::VERSION::STRING.to_f}]"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/generators/perfm/sidekiq_gvl_metrics/templates/create_perfm_sidekiq_gvl_metrics.rb.erb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreatePerfmSidekiqGvlMetrics < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
create_table :perfm_sidekiq_gvl_metrics do |t|
|
4
|
+
t.float :gc_ms
|
5
|
+
t.float :run_ms
|
6
|
+
t.float :idle_ms
|
7
|
+
t.float :stall_ms
|
8
|
+
t.float :io_percent
|
9
|
+
t.string :job_class
|
10
|
+
t.string :queue
|
11
|
+
|
12
|
+
t.index [:job_class, :queue]
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/perfm/agent.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
module Perfm
|
2
2
|
class Agent
|
3
|
-
attr_reader :config, :queue
|
3
|
+
attr_reader :config, :queue, :sidekiq_queue
|
4
4
|
|
5
5
|
def initialize(config, storage)
|
6
6
|
@config = config
|
7
7
|
@queue = Queue.new(storage)
|
8
|
+
@sidekiq_queue = Queue.new(storage)
|
8
9
|
end
|
9
10
|
|
10
11
|
def push_metrics(data)
|
11
12
|
queue.push_metrics(data)
|
12
13
|
end
|
14
|
+
|
15
|
+
def push_sidekiq_metrics(data)
|
16
|
+
sidekiq_queue.push_metrics(data)
|
17
|
+
end
|
13
18
|
end
|
14
19
|
end
|
data/lib/perfm/configuration.rb
CHANGED
@@ -6,6 +6,7 @@ module Perfm
|
|
6
6
|
enabled: true,
|
7
7
|
monitor_sidekiq: false,
|
8
8
|
monitor_gvl: false,
|
9
|
+
monitor_sidekiq_gvl: false,
|
9
10
|
monitor_sidekiq_queues: false,
|
10
11
|
storage: :api,
|
11
12
|
api_url: nil,
|
@@ -20,6 +21,10 @@ module Perfm
|
|
20
21
|
enabled? && monitor_gvl
|
21
22
|
end
|
22
23
|
|
24
|
+
def monitor_sidekiq_gvl?
|
25
|
+
enabled? && monitor_sidekiq_gvl
|
26
|
+
end
|
27
|
+
|
23
28
|
def enabled?
|
24
29
|
enabled
|
25
30
|
end
|
data/lib/perfm/engine.rb
CHANGED
@@ -6,6 +6,14 @@ module Perfm
|
|
6
6
|
if Perfm.configuration.monitor_gvl?
|
7
7
|
app.config.middleware.insert(0, Perfm::Middleware::GvlInstrumentation)
|
8
8
|
end
|
9
|
+
|
10
|
+
if Perfm.configuration.monitor_sidekiq_gvl? && defined?(Sidekiq)
|
11
|
+
Sidekiq.configure_server do |config|
|
12
|
+
config.server_middleware do |chain|
|
13
|
+
chain.add Perfm::Middleware::SidekiqGvlInstrumentation
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
9
17
|
end
|
10
18
|
end
|
11
19
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "gvl_timing"
|
2
|
+
|
3
|
+
module Perfm
|
4
|
+
module Middleware
|
5
|
+
class SidekiqGvlInstrumentation
|
6
|
+
def call(job_instance, job_payload, queue)
|
7
|
+
before_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
8
|
+
before_gc_time = GC.total_time
|
9
|
+
|
10
|
+
timer = GVLTiming.measure do
|
11
|
+
yield
|
12
|
+
end
|
13
|
+
|
14
|
+
total_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - before_time
|
15
|
+
gc_time = GC.total_time - before_gc_time
|
16
|
+
|
17
|
+
begin
|
18
|
+
data = {
|
19
|
+
gc_ms: (gc_time / 1_000_000.0).round(2),
|
20
|
+
run_ms: (timer.cpu_duration * 1000.0).round(2),
|
21
|
+
idle_ms: (timer.idle_duration * 1000.0).round(2),
|
22
|
+
stall_ms: (timer.stalled_duration * 1000.0).round(2),
|
23
|
+
io_percent: (timer.idle_duration / total_time * 100.0).round(1),
|
24
|
+
job_class: job_payload["class"],
|
25
|
+
queue: queue
|
26
|
+
}
|
27
|
+
|
28
|
+
Perfm.agent.push_sidekiq_metrics(data)
|
29
|
+
rescue => e
|
30
|
+
puts "Sidekiq GVL metrics collection failed: #{e.message}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Perfm
|
4
|
+
class SidekiqGvlMetricsAnalyzer
|
5
|
+
attr_reader :start_time, :end_time, :query
|
6
|
+
|
7
|
+
def initialize(start_time: nil, end_time: nil, job_class: nil, queue: nil)
|
8
|
+
@start_time = start_time
|
9
|
+
@end_time = end_time
|
10
|
+
@query = SidekiqGvlMetric.where(true)
|
11
|
+
@query = @query.where(job_class: job_class) if job_class
|
12
|
+
@query = @query.where(queue: queue) if queue
|
13
|
+
@query = @query.where(created_at: start_time..end_time) if start_time && end_time
|
14
|
+
end
|
15
|
+
|
16
|
+
def analyze
|
17
|
+
return {} if query.count.zero?
|
18
|
+
|
19
|
+
result = {}
|
20
|
+
result[:total_io_percentage] = total_io_percentage
|
21
|
+
result[:total_io_and_stall_percentage] = total_io_and_stall_percentage
|
22
|
+
result[:average_processing_time_ms] = average_processing_time_ms
|
23
|
+
result[:average_stall_ms] = average_stall_ms
|
24
|
+
result[:job_count] = job_count
|
25
|
+
result[:time_range] = time_range_info
|
26
|
+
result[:by_job_class] = metrics_by_job_class
|
27
|
+
result[:by_queue] = metrics_by_queue
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def average_io_ms
|
34
|
+
query.average(:idle_ms).to_f
|
35
|
+
end
|
36
|
+
|
37
|
+
def average_stall_ms
|
38
|
+
query.average(:stall_ms).to_f
|
39
|
+
end
|
40
|
+
|
41
|
+
def average_run_ms
|
42
|
+
query.average(:run_ms).to_f
|
43
|
+
end
|
44
|
+
|
45
|
+
def average_processing_time_ms
|
46
|
+
average_run_ms + average_io_ms + average_stall_ms
|
47
|
+
end
|
48
|
+
|
49
|
+
def job_count
|
50
|
+
query.count
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_io_percentage
|
54
|
+
(average_io_ms / average_processing_time_ms * 100).round(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
def total_io_and_stall_percentage
|
58
|
+
((average_io_ms + average_stall_ms) / average_processing_time_ms * 100).round(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
def time_range_info
|
62
|
+
range_start = start_time || query.minimum(:created_at)
|
63
|
+
range_end = end_time || query.maximum(:created_at)
|
64
|
+
duration = range_end - range_start
|
65
|
+
|
66
|
+
{
|
67
|
+
start_time: range_start.iso8601,
|
68
|
+
end_time: range_end.iso8601,
|
69
|
+
duration_seconds: duration.to_i
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def metrics_by_job_class
|
74
|
+
results = {}
|
75
|
+
|
76
|
+
job_classes = query.distinct.pluck(:job_class)
|
77
|
+
job_classes.each do |job_class|
|
78
|
+
job_query = query.where(job_class: job_class)
|
79
|
+
|
80
|
+
job_avg_processing_ms = job_query.average(:run_ms).to_f +
|
81
|
+
job_query.average(:idle_ms).to_f +
|
82
|
+
job_query.average(:stall_ms).to_f
|
83
|
+
|
84
|
+
job_avg_io_ms = job_query.average(:idle_ms).to_f
|
85
|
+
job_avg_stall_ms = job_query.average(:stall_ms).to_f
|
86
|
+
|
87
|
+
results[job_class] = {
|
88
|
+
count: job_query.count,
|
89
|
+
average_processing_time_ms: job_avg_processing_ms.round(1),
|
90
|
+
average_io_ms: job_avg_io_ms.round(1),
|
91
|
+
average_stall_ms: job_avg_stall_ms.round(1),
|
92
|
+
io_percentage: (job_avg_io_ms / job_avg_processing_ms * 100).round(1),
|
93
|
+
io_and_stall_percentage: ((job_avg_io_ms + job_avg_stall_ms) / job_avg_processing_ms * 100).round(1)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
results
|
98
|
+
end
|
99
|
+
|
100
|
+
def metrics_by_queue
|
101
|
+
results = {}
|
102
|
+
|
103
|
+
queues = query.distinct.pluck(:queue)
|
104
|
+
queues.each do |queue|
|
105
|
+
queue_query = query.where(queue: queue)
|
106
|
+
|
107
|
+
queue_avg_processing_ms = queue_query.average(:run_ms).to_f +
|
108
|
+
queue_query.average(:idle_ms).to_f +
|
109
|
+
queue_query.average(:stall_ms).to_f
|
110
|
+
|
111
|
+
queue_avg_io_ms = queue_query.average(:idle_ms).to_f
|
112
|
+
queue_avg_stall_ms = queue_query.average(:stall_ms).to_f
|
113
|
+
|
114
|
+
results[queue] = {
|
115
|
+
count: queue_query.count,
|
116
|
+
average_processing_time_ms: queue_avg_processing_ms.round(1),
|
117
|
+
average_io_ms: queue_avg_io_ms.round(1),
|
118
|
+
average_stall_ms: queue_avg_stall_ms.round(1),
|
119
|
+
io_percentage: (queue_avg_io_ms / queue_avg_processing_ms * 100).round(1),
|
120
|
+
io_and_stall_percentage: ((queue_avg_io_ms + queue_avg_stall_ms) / queue_avg_processing_ms * 100).round(1)
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
results
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/perfm/storage/local.rb
CHANGED
data/lib/perfm/version.rb
CHANGED
data/lib/perfm.rb
CHANGED
@@ -11,6 +11,7 @@ module Perfm
|
|
11
11
|
autoload :Queue, "perfm/queue"
|
12
12
|
autoload :Agent, "perfm/agent"
|
13
13
|
autoload :GvlMetricsAnalyzer, "perfm/gvl_metrics_analyzer"
|
14
|
+
autoload :SidekiqGvlMetricsAnalyzer, "perfm/sidekiq_gvl_metrics_analyzer"
|
14
15
|
|
15
16
|
module Storage
|
16
17
|
autoload :Base, "perfm/storage/base"
|
@@ -20,6 +21,7 @@ module Perfm
|
|
20
21
|
|
21
22
|
module Middleware
|
22
23
|
autoload :GvlInstrumentation, "perfm/middleware/gvl_instrumentation"
|
24
|
+
autoload :SidekiqGvlInstrumentation, "perfm/middleware/sidekiq_gvl_instrumentation"
|
23
25
|
end
|
24
26
|
|
25
27
|
class << self
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perfm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vishnu M
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -114,7 +114,7 @@ dependencies:
|
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '1.21'
|
117
|
-
description:
|
117
|
+
description: Perfm aims to be a performance monitoring tool for Ruby on Rails applications.
|
118
118
|
email:
|
119
119
|
- vishnu.m@bigbinary.com
|
120
120
|
executables: []
|
@@ -126,8 +126,11 @@ files:
|
|
126
126
|
- Rakefile
|
127
127
|
- app/models/perfm/application_record.rb
|
128
128
|
- app/models/perfm/gvl_metric.rb
|
129
|
+
- app/models/perfm/sidekiq_gvl_metric.rb
|
129
130
|
- lib/generators/perfm/install/install_generator.rb
|
130
131
|
- lib/generators/perfm/install/templates/create_perfm_gvl_metrics.rb.erb
|
132
|
+
- lib/generators/perfm/sidekiq_gvl_metrics/sidekiq_gvl_metrics_generator.rb
|
133
|
+
- lib/generators/perfm/sidekiq_gvl_metrics/templates/create_perfm_sidekiq_gvl_metrics.rb.erb
|
131
134
|
- lib/generators/perfm/uninstall/templates/drop_perfm_gvl_metrics.rb.erb
|
132
135
|
- lib/generators/perfm/uninstall/uninstall_generator.rb
|
133
136
|
- lib/perfm.rb
|
@@ -140,20 +143,22 @@ files:
|
|
140
143
|
- lib/perfm/heap_dumper.rb
|
141
144
|
- lib/perfm/metrics/sidekiq.rb
|
142
145
|
- lib/perfm/middleware/gvl_instrumentation.rb
|
146
|
+
- lib/perfm/middleware/sidekiq_gvl_instrumentation.rb
|
143
147
|
- lib/perfm/pid_store.rb
|
144
148
|
- lib/perfm/queue.rb
|
145
149
|
- lib/perfm/queue_latency.rb
|
150
|
+
- lib/perfm/sidekiq_gvl_metrics_analyzer.rb
|
146
151
|
- lib/perfm/storage/api.rb
|
147
152
|
- lib/perfm/storage/base.rb
|
148
153
|
- lib/perfm/storage/local.rb
|
149
154
|
- lib/perfm/version.rb
|
150
|
-
homepage: https://github.com/
|
155
|
+
homepage: https://github.com/bigbinary/perfm
|
151
156
|
licenses:
|
152
157
|
- MIT
|
153
158
|
metadata:
|
154
|
-
homepage_uri: https://github.com/
|
155
|
-
source_code_uri: https://github.com/
|
156
|
-
changelog_uri: https://github.com/
|
159
|
+
homepage_uri: https://github.com/bigbinary/perfm
|
160
|
+
source_code_uri: https://github.com/bigbinary/perfm
|
161
|
+
changelog_uri: https://github.com/bigbinary/perfm/blob/master/CHANGELOG.md
|
157
162
|
post_install_message:
|
158
163
|
rdoc_options: []
|
159
164
|
require_paths:
|
@@ -169,8 +174,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
174
|
- !ruby/object:Gem::Version
|
170
175
|
version: '0'
|
171
176
|
requirements: []
|
172
|
-
rubygems_version: 3.4.
|
177
|
+
rubygems_version: 3.4.19
|
173
178
|
signing_key:
|
174
179
|
specification_version: 4
|
175
|
-
summary:
|
180
|
+
summary: Perfm aims to be a performance monitoring tool for Ruby on Rails applications.
|
181
|
+
Currently, it has support for GVL instrumentation and provides analytics to help
|
182
|
+
optimize Puma thread concurrency settings based on the collected GVL data.
|
176
183
|
test_files: []
|