{"id":29705777,"url":"https://github.com/bigbinary/perfm","last_synced_at":"2025-07-23T15:08:51.869Z","repository":{"id":291574898,"uuid":"978067772","full_name":"bigbinary/perfm","owner":"bigbinary","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-05T11:47:22.000Z","size":29,"stargazers_count":46,"open_issues_count":0,"forks_count":1,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-07-01T03:18:08.530Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bigbinary.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-05T12:24:17.000Z","updated_at":"2025-06-15T01:22:25.000Z","dependencies_parsed_at":"2025-05-05T12:49:24.725Z","dependency_job_id":"dc323fc4-f949-43ba-b231-56202ea5df0b","html_url":"https://github.com/bigbinary/perfm","commit_stats":null,"previous_names":["bigbinary/perfm"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bigbinary/perfm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigbinary%2Fperfm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigbinary%2Fperfm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigbinary%2Fperfm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigbinary%2Fperfm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bigbinary","download_url":"https://codeload.github.com/bigbinary/perfm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigbinary%2Fperfm/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266700039,"owners_count":23970616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-23T15:08:50.323Z","updated_at":"2025-07-23T15:08:51.851Z","avatar_url":"https://github.com/bigbinary.png","language":"Ruby","readme":"# Perfm\n\nPerfm 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.\n\n## Requirements\n\n- Ruby: MRI 3.2+\n\nThis 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.\n\n## Installation\n\nAdd perfm to your Gemfile.\n\n```ruby\ngem 'perfm'\n```\n\nTo set up GVL instrumentation run the following command:\n\n```bash\nbin/rails generate perfm:install\n```\n\nThis will create a migration file with a table to store the GVL metrics. Run the migration and configure the gem as described below.\n\n## Configuration\n\nConfigure Perfm in an initializer:\n\n```ruby\nPerfm.configure do |config|\n  config.enabled = true\n  config.monitor_gvl = true\n  config.storage = :local\nend\n\nPerfm.setup!\n\n```\n\nWhen `monitor_gvl` is enabled, perfm adds a Rack middleware to log GVL metrics for each request. The metrics are stored in the database.\n\nWe just need around `20000` datapoints(i.e requests) to get an idea of the app's workload. So the `monitor_gvl` config can be disabled after that. You can control the value via an ENV variable if you prefer.\n\n## Analysis\n\n```ruby\ngvl_metrics_analyzer = Perfm::GvlMetricsAnalyzer.new(\n  start_time: 5.days.ago,\n  end_time: Time.current\n)\n\ngvl_metrics_analyzer.analyze\n\n# Write to file\nFile.write(\n \"tmp/perfm/gvl_analysis_#{Time.current.strftime('%Y%m%d_%H%M%S')}.json\",\n  JSON.pretty_generate(gvl_metrics_analyzer.analyze)\n)\n```\n\nThis will print the following metrics:\n\n- `total_io_percentage`: Percentage of time spent doing I/O operations\n- `total_io_and_stall_percentage`: Percentage of time spent in I/O operations(idle time) and GVL stalls combined\n- `average_response_time_ms`: Average response time in milliseconds per request\n- `average_stall_ms`: Average GVL stall time in milliseconds per request\n- `average_gc_ms`: Average garbage collection time in milliseconds per request\n- `request_count`: Total number of requests analyzed\n- `time_range`: Details about the analysis period including:\n  - `start_time`\n  - `end_time`\n  - `duration_seconds`\n\nAfter analysis, you can drop the table to save space. The following command generates a migration to drop the table.\n\n```bash\nbin/rails generate perfm:uninstall\n```\n\n## Beta Features\n\nThe following features are currently in beta and may have limited functionality or be subject to change.\n\n### Perfm queue latency monitor\n\nThe 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.\n\nruby\n\n```ruby\nPerfm.configure do |config|\n  # Other configurations...\n  config.monitor_sidekiq_queues = true\nend\n```\n\nWhen enabled, Perfm will monitor your Sidekiq queues and raise a `Perfm::Errors::LatencyExceededError` when the queue latency exceeds the threshold.\n\n#### Queue Naming Convention\n\nPerfm expects queues that need latency monitoring to follow this naming pattern:\n\n- `within_X_seconds` (e.g., within_5_seconds)\n- `within_X_minutes` (e.g., within_2_minutes)\n- `within_X_hours` (e.g., within_1_hours)\n\n### Sidekiq GVL Instrumentation\n\nTo enable GVL instrumentation for Sidekiq, first run the generator to add migrations for the required table to store the metrics.\n\n```bash\nbin/rails generate perfm:sidekiq_gvl_metrics\n```\n\nThen enable the `monitor_sidekiq_gvl` configuration in your initializer.\n\n```ruby\nPerfm.configure do |config|\n  config.monitor_sidekiq_gvl = true\nend\n```\n\nWhen 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.\n\n```ruby\nPerfm::SidekiqGvlMetric.calculate_queue_io_percentage(\"within_5_seconds\")\n```\n\n### Heap analyzer\n\n### Generate and Store Heap Dumps via ActiveStorage\n\nPerfm has a heap dump generator which can be used to generate heap dumps from running Puma worker processes and storing them via ActiveStorage. This can be useful for debugging memory leaks. We can generate three dumps separate by a time period of lets say 15 minutes and analyze it via heapy or sheap.\n\n_Note: The process of heap dump generation can increase the memory usage._\n\n#### Puma configuration changes:\n\nAdd the following to your `config/puma.rb`:\n\n```ruby\non_worker_boot do\n  Perfm::PidStore.instance.add_worker_pid(Process.pid)\nend\n\non_worker_shutdown do\n  Perfm::PidStore.instance.clear\nend\n```\n\nWe need to keep track of pid of each worker process so that we inject code to generate heap dump in each worker process using [rbtrace](https://github.com/tmm1/rbtrace)\n\n#### Route setup\n\n```ruby\n# config/routes.rb\nRails.application.routes.draw do\n  namespace :perfm do\n    namespace :admin do\n      resources :heap_dumps, only: :create\n    end\n  end\nend\n```\n\n#### Controller to generate heap dumps\n\nAs we need to invoke rbtrace from the same process, we'll use a controller itself to invoke the `HeapDumper`.\n\n```ruby\nclass Perfm::Admin::HeapDumpsController \u003c ActionController::Base\n  skip_forgery_protection\n  before_action :authenticate_admin\n\n  def create\n    blob = Perfm::HeapDumper.generate\n\n    render json: {\n      status: \"success\",\n      message: \"Heap dump generated successfully\",\n      blob_id: blob.id,\n      filename: blob.filename.to_s\n    }\n  rescue Perfm::HeapDumper::Error =\u003e e\n    render json: { status: \"error\", message: e.message }, status: :unprocessable_entity\n  end\n\n  private\n\n  def authenticate_admin\n    return if Rails.env.development?\n\n    unless valid_token?(request.headers[\"X-Perfm-Token\"])\n      head :unauthorized\n    end\n  end\n\n  def valid_token?(token)\n    return false if token.blank? || Perfm.configuration.admin_token.blank?\n\n    ActiveSupport::SecurityUtils.secure_compare(\n      token,\n      Perfm.configuration.admin_token\n    )\n  end\nend\n```\n\n#### Usage\n\n```bash\ncurl -X POST https://your-app.com/perfm/admin/heap_dumps -H \"X-Perfm-Token: your-secure-token\"\n```\n\nThe generated heap dump will be stored via ActiveStorage and the response includes the blob ID and filename for later reference.\n\n#### Configuration\n\nConfigure the admin token in your Perfm initializer:\n\n```ruby\n# config/initializers/perfm.rb\nPerfm.configure do |config|\n  config.admin_token = ENV[\"PERFM_ADMIN_TOKEN\"]\nend\n```\n\nThe generated heap dumps can be downloaded and analyzed using [heapy](https://github.com/zombocom/heapy)\n\nWe're planning to add a heap analyzer within perfm itself to make the process seamless.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigbinary%2Fperfm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbigbinary%2Fperfm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigbinary%2Fperfm/lists"}