diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..30a021f9 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,16 @@ +env: + browser: true + node: true + es6: true +extends: 'eslint:recommended' +globals: + Atomics: readonly + SharedArrayBuffer: readonly +parserOptions: + ecmaFeatures: + jsx: true + ecmaVersion: 2018 + sourceType: module +plugins: + - react +rules: {} diff --git a/.gitignore b/.gitignore index df2840a6..657ce3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ latest.dump /public/packs-test /node_modules config/database.yml +config/influxdb.yml # Ignore storybook static site generation storybook-static/ @@ -52,4 +53,4 @@ package-lock.json .idea/ #sitemap -/public/sitemap.xml.gz \ No newline at end of file +/public/sitemap.xml.gz diff --git a/.rspec_parallel b/.rspec_parallel new file mode 100644 index 00000000..da9c7f79 --- /dev/null +++ b/.rspec_parallel @@ -0,0 +1,3 @@ +--format progress +--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log +--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log diff --git a/.ruby-version b/.ruby-version index ec1cf33c..1f7da99d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.3 +2.7.7 diff --git a/Gemfile b/Gemfile index 1a5f4c0b..a267bf1c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ # rubocop:disable LineLength source "https://rubygems.org" -ruby "2.6.3" +ruby "2.7.7" # Enforce git to transmitted via https. # workaround until bundler 2.0 is released. @@ -10,6 +10,7 @@ git_source(:github) do |repo_name| end group :production do + gem "honeycomb-rails" gem "nakayoshi_fork" end @@ -50,7 +51,6 @@ gem "front_matter_parser", "~> 0.2" gem "gemoji", "~> 3.0.0" gem "gibbon", "~> 2.2" gem "google-api-client", "~> 0.27" -gem "honeycomb-rails" gem "html_truncator", "~> 0.4" gem "httparty", "~> 0.16" gem "inline_svg", "~> 1.3" @@ -65,6 +65,7 @@ gem "omniauth", "~> 1.9" gem "omniauth-github", "~> 1.3" gem "omniauth-twitter", "~> 1.4" gem "pg", "~> 1.1" +gem "prometheus_exporter", "~> 2.0" gem "pry", "~> 0.12" gem "pry-rails", "~> 0.3" gem "puma", "~> 3.12" @@ -72,6 +73,7 @@ gem "pundit", "~> 2.0" gem "pusher", "~> 1.3" gem "pusher-push-notifications", "~> 1.0" gem "rack-host-redirect", "~> 1.3" +gem "rack-mini-profiler", "~> 3.1", require: false gem "rack-timeout", "~> 0.5" gem "rails", "~> 5.1.6" gem "rails-assets-airbrake-js-client", "~> 1.5", source: "https://rails-assets.org" @@ -89,7 +91,7 @@ gem "sdoc", "~> 1.0", group: :doc gem "serviceworker-rails", "~> 0.5" gem "share_meow_client", "~> 0.1" gem "sitemap_generator", "~> 6.0" -gem "skylight", "~> 3.1" +gem "skylight", "~> 4.3" gem "slack-notifier", "~> 2.3" gem "sprockets", "~> 3.7" gem "staccato", "~> 0.5" @@ -103,6 +105,11 @@ gem "validate_url", "~> 1.0" gem "webpacker", "~> 3.5" gem "webpush", "~> 0.3" +group :localproduction, :development do + gem 'meta_request', "~> 0.7" + gem 'newrelic_rpm', "~> 9.6" +end + group :development do gem "better_errors", "~> 2.5" gem "binding_of_caller", "~> 0.8" @@ -132,7 +139,9 @@ group :development, :test do gem "rubocop-rspec", "~> 1.31" gem "spring", "~> 2.0" gem "spring-commands-rspec", "~> 1.0" + gem "tty-command", "~> 0.10" gem "vcr", "~> 4.0" + gem "warning", "~> 1.3" end group :test do @@ -141,17 +150,18 @@ group :test do gem "database_cleaner", "~> 1.7" gem "factory_bot_rails", "~> 4.11" gem "fake_stripe", "~> 0.2" + gem "influxer", "~> 1.4" gem "launchy", "~> 2.4" gem "pundit-matchers", "~> 1.6" gem "rails-controller-testing", "~> 1.0" - gem "ruby-prof", "~> 0.17", require: false + gem "ruby-prof", "~> 1.6", require: false gem "selenium-webdriver", "~> 3.141" gem "shoulda-matchers", "4.0.0.rc1", require: false gem "simplecov", "~> 0.16", require: false gem "sinatra", "~> 2.0" gem "stackprof", "~> 0.2", require: false, platforms: :ruby gem "stripe-ruby-mock", "~> 2.5", require: "stripe_mock" - gem "test-prof", "~> 0.7" + gem "test-prof", "~> 1.3" gem "timecop", "~> 0.9" gem "webmock", "~> 3.5" gem "zonebie", "~> 0.6.1" diff --git a/Gemfile.lock b/Gemfile.lock index c7c050cf..22ba49c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -112,10 +112,12 @@ GEM json (~> 1.8) ancestry (3.0.5) activerecord (>= 3.2.0) + anyway_config (2.5.4) + ruby-next-core (>= 0.14.0) approvals (0.0.24) nokogiri (~> 1.6) thor (~> 0.18) - archive-zip (0.11.0) + archive-zip (0.12.0) io-like (~> 0.3.0) arel (8.0.0) ast (2.4.0) @@ -137,6 +139,7 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) + base64 (0.2.0) bcrypt (3.1.12) benchmark-ips (2.7.2) better_errors (2.5.0) @@ -176,13 +179,14 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (11.0.0) - capybara (3.13.2) + capybara (3.39.2) addressable + matrix mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.2) + regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) carrierwave (1.3.1) activemodel (>= 4.0.0) @@ -194,9 +198,8 @@ GEM fastimage caze (0.2.2) activesupport (>= 3) - childprocess (0.9.0) - ffi (~> 1.0, >= 1.0.11) - chromedriver-helper (2.1.0) + childprocess (3.0.0) + chromedriver-helper (2.1.1) archive-zip (~> 0.10) nokogiri (~> 1.8) cloudinary (1.11.1) @@ -212,7 +215,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.5) + concurrent-ruby (1.2.2) connection_pool (2.2.2) counter_culture (2.1.2) activerecord (>= 3.0.0) @@ -317,7 +320,7 @@ GEM faraday_middleware (>= 0.9) loofah (>= 2.0) sax-machine (>= 1.0) - ffi (1.9.25) + ffi (1.16.3) figaro (1.1.1) thor (~> 0.14) fission (0.5.0) @@ -536,14 +539,19 @@ GEM mime-types (~> 3.0) multi_xml (>= 0.5.2) httpclient (2.8.3) - i18n (1.6.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) ice_nine (0.11.2) inflecto (0.0.2) + influxdb (0.8.1) + influxer (1.4.0) + activemodel (>= 3.2.0) + anyway_config (>= 2.0) + influxdb (~> 0.8) inline_svg (1.3.1) activesupport (>= 3.0) nokogiri (>= 1.6) - io-like (0.3.0) + io-like (0.3.1) ipaddress (0.8.3) jaro_winkler (1.5.2) jbuilder (2.8.0) @@ -588,17 +596,21 @@ GEM lumberjack (1.0.13) mail (2.7.1) mini_mime (>= 0.1.1) + matrix (0.4.2) memoist (0.16.0) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) memory_profiler (0.9.12) + meta_request (0.7.4) + rack-contrib (>= 1.1, < 3) + railties (>= 3.0.0, < 7.1) method_source (0.9.2) mime-types (3.2.2) mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) - mini_mime (1.0.1) - mini_portile2 (2.4.0) - minitest (5.11.3) + mini_mime (1.1.5) + mini_portile2 (2.8.5) + minitest (5.20.0) momentjs-rails (2.20.1) railties (>= 3.1) msgpack (1.2.4) @@ -612,9 +624,12 @@ GEM net-http-persistent (3.0.0) connection_pool (~> 2.2) netrc (0.11.0) + newrelic_rpm (9.6.0) + base64 nio4r (2.3.1) - nokogiri (1.10.1) - mini_portile2 (~> 2.4.0) + nokogiri (1.15.5) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) @@ -644,13 +659,17 @@ GEM rack orm_adapter (0.5.0) os (1.0.0) - parallel (1.13.0) - parallel_tests (2.27.1) + parallel (1.23.0) + parallel_tests (2.32.0) parallel parser (2.6.0.0) ast (~> 2.4.0) + pastel (0.8.0) + tty-color (~> 0.5) pg (1.1.4) powerpack (0.1.2) + prometheus_exporter (2.0.8) + webrick pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -659,7 +678,7 @@ GEM pry (~> 0.10) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (3.0.3) + public_suffix (3.1.1) puma (3.12.0) pundit (2.0.1) activesupport (>= 3.0.0) @@ -674,15 +693,20 @@ GEM rest-client pusher-signature (0.1.8) raabro (1.1.6) - rack (2.0.6) + racc (1.7.3) + rack (2.2.8) + rack-contrib (2.4.0) + rack (< 4) rack-host-redirect (1.3.0) rack + rack-mini-profiler (3.1.1) + rack (>= 1.2.0) rack-protection (2.0.4) rack rack-proxy (0.6.5) rack - rack-test (1.1.0) - rack (>= 1.0, < 3) + rack-test (2.1.0) + rack (>= 1.3) rack-timeout (0.5.1) rails (5.1.6.2) actioncable (= 5.1.6.2) @@ -729,7 +753,7 @@ GEM recaptcha (4.13.1) json redcarpet (3.4.0) - regexp_parser (1.3.0) + regexp_parser (2.8.2) representable (3.0.4) declarative (< 0.1.0) declarative-option (< 0.2.0) @@ -781,10 +805,11 @@ GEM unicode-display_width (~> 1.4.0) rubocop-rspec (1.31.0) rubocop (>= 0.60.0) - ruby-prof (0.17.0) + ruby-next-core (0.15.3) + ruby-prof (1.6.3) ruby-progressbar (1.10.0) ruby_dep (1.5.0) - rubyzip (1.2.2) + rubyzip (1.3.0) s3_direct_upload (0.1.7) coffee-rails (>= 3.1) jquery-fileupload-rails (~> 0.4.1) @@ -816,9 +841,9 @@ GEM sdoc (1.0.0) rdoc (>= 5.0) selectize-rails (0.12.6) - selenium-webdriver (3.141.0) - childprocess (~> 0.5) - rubyzip (~> 1.2, >= 1.2.2) + selenium-webdriver (3.142.7) + childprocess (>= 0.5, < 4.0) + rubyzip (>= 1.2.2) serviceworker-rails (0.5.5) railties (>= 3.1) share_meow_client (0.1.3) @@ -843,9 +868,9 @@ GEM tilt (~> 2.0) sitemap_generator (6.0.2) builder (~> 3.0) - skylight (3.1.4) - skylight-core (= 3.1.4) - skylight-core (3.1.4) + skylight (4.3.2) + skylight-core (= 4.3.2) + skylight-core (4.3.2) activesupport (>= 4.2.0) slack-notifier (2.3.2) smart_properties (1.13.1) @@ -861,7 +886,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) staccato (0.5.1) - stackprof (0.2.12) + stackprof (0.2.25) storext (2.2.2) rails (>= 4.0, < 6.0) virtus @@ -872,7 +897,7 @@ GEM dante (>= 0.2.0) multi_json (~> 1.0) stripe (>= 2.0.3) - test-prof (0.7.3) + test-prof (1.3.0) thor (0.20.3) thread_safe (0.3.6) tilt (2.0.9) @@ -880,6 +905,9 @@ GEM msgpack (~> 1.0) timecop (0.9.1) trollop (2.1.2) + tty-color (0.6.0) + tty-command (0.10.1) + pastel (~> 0.8) twilio-ruby (5.21.0) faraday (~> 0.9) jwt (>= 1.5, <= 2.5) @@ -895,7 +923,7 @@ GEM multipart-post (~> 2.0) naught (~> 1.0) simple_oauth (~> 0.3.0) - tzinfo (1.2.5) + tzinfo (1.2.11) thread_safe (~> 0.1) uber (0.1.0) uglifier (4.1.20) @@ -916,6 +944,7 @@ GEM equalizer (~> 0.0, >= 0.0.9) warden (1.2.8) rack (>= 2.0.6) + warning (1.3.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -932,6 +961,7 @@ GEM webpush (0.3.2) hkdf (~> 0.2) jwt + webrick (1.8.1) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) @@ -1004,6 +1034,7 @@ DEPENDENCIES honeycomb-rails html_truncator (~> 0.4) httparty (~> 0.16) + influxer (~> 1.4) inline_svg (~> 1.3) jbuilder (~> 2.8) jquery-rails (~> 4.3) @@ -1012,7 +1043,9 @@ DEPENDENCIES libhoney (~> 1.11) liquid (~> 4.0) memory_profiler (~> 0.9) + meta_request (~> 0.7) nakayoshi_fork + newrelic_rpm (~> 9.6) nokogiri (~> 1.10) octokit (~> 4.13) omniauth (~> 1.9) @@ -1020,6 +1053,7 @@ DEPENDENCIES omniauth-twitter (~> 1.4) parallel_tests (~> 2.27) pg (~> 1.1) + prometheus_exporter (~> 2.0) pry (~> 0.12) pry-byebug (~> 3.7) pry-rails (~> 0.3) @@ -1029,6 +1063,7 @@ DEPENDENCIES pusher (~> 1.3) pusher-push-notifications (~> 1.0) rack-host-redirect (~> 1.3) + rack-mini-profiler (~> 3.1) rack-timeout (~> 0.5) rails (~> 5.1.6) rails-assets-airbrake-js-client (~> 1.5)! @@ -1044,7 +1079,7 @@ DEPENDENCIES rspec-retry (~> 0.6) rubocop (~> 0.63) rubocop-rspec (~> 1.31) - ruby-prof (~> 0.17) + ruby-prof (~> 1.6) rubyzip (~> 1.2, >= 1.2.2) s3_direct_upload (~> 0.1) sail (~> 1.5) @@ -1057,7 +1092,7 @@ DEPENDENCIES simplecov (~> 0.16) sinatra (~> 2.0) sitemap_generator (~> 6.0) - skylight (~> 3.1) + skylight (~> 4.3) slack-notifier (~> 2.3) spring (~> 2.0) spring-commands-rspec (~> 1.0) @@ -1067,14 +1102,16 @@ DEPENDENCIES storext (~> 2.2) stripe (~> 4.8) stripe-ruby-mock (~> 2.5) - test-prof (~> 0.7) + test-prof (~> 1.3) timber (~> 2.6) timecop (~> 0.9) + tty-command (~> 0.10) twilio-ruby (~> 5.21) twitter (~> 6.2) uglifier (~> 4.1) validate_url (~> 1.0) vcr (~> 4.0) + warning (~> 1.3) web-console (~> 3.7) webmock (~> 3.5) webpacker (~> 3.5) @@ -1082,7 +1119,7 @@ DEPENDENCIES zonebie (~> 0.6.1) RUBY VERSION - ruby 2.6.3p62 + ruby 2.7.7p221 BUNDLED WITH 1.17.3 diff --git a/Procfile.dev b/Procfile.dev index f6093523..b1cd4045 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,4 @@ web: bin/rails s -p 3000 webpacker: ./bin/webpack-dev-server job: bin/rake jobs:work +prometheus_exporter: bundle exec prometheus_exporter diff --git a/Procfile.localproduction b/Procfile.localproduction new file mode 100644 index 00000000..b1cd4045 --- /dev/null +++ b/Procfile.localproduction @@ -0,0 +1,4 @@ +web: bin/rails s -p 3000 +webpacker: ./bin/webpack-dev-server +job: bin/rake jobs:work +prometheus_exporter: bundle exec prometheus_exporter diff --git a/app/metrics/test_duration_metric.rb b/app/metrics/test_duration_metric.rb new file mode 100644 index 00000000..69758610 --- /dev/null +++ b/app/metrics/test_duration_metric.rb @@ -0,0 +1,10 @@ +class TestDurationMetric < Influxer::Metrics + set_series :test + + tags :user + + attributes :run_time_seconds + + validates :user, :run_time_seconds, presence: true + validates :run_time_seconds, numericality: true +end diff --git a/app/services/exporter/service.rb b/app/services/exporter/service.rb index 44058710..fc6c809e 100644 --- a/app/services/exporter/service.rb +++ b/app/services/exporter/service.rb @@ -5,8 +5,8 @@ class Service attr_reader :user EXPORTERS = [ - Articles, - Comments, + Exporter::Articles, + Exporter::Comments, ].freeze def initialize(user) diff --git a/app/views/stories/_main_stories_feed.html.erb b/app/views/stories/_main_stories_feed.html.erb index b6cd0a65..3e175010 100644 --- a/app/views/stories/_main_stories_feed.html.erb +++ b/app/views/stories/_main_stories_feed.html.erb @@ -55,7 +55,9 @@ <% if !user_signed_in? && i == 4 %> <%= render "stories/sign_in_invitation" %> <% end %> - <%= render "articles/single_story", story: story %> + <% cache story do %> + <%= render "articles/single_story", story: story %> + <% end %> <% end %> <% end %> <% if @stories.size > 1 %> diff --git a/bin/startup b/bin/startup index c7e84efb..feee8bff 100755 --- a/bin/startup +++ b/bin/startup @@ -12,5 +12,10 @@ end chdir APP_ROOT do puts "== STARTING UP ==" - system! "foreman start -f Procfile.dev" + if ENV['RAILS_ENV'] == 'localproduction' + system! "bundle exec rake assets:precompile" + system! "foreman start -f Procfile.localproduction" + else + system! "foreman start -f Procfile.dev" + end end diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server index 63edc01e..c79289f9 100755 --- a/bin/webpack-dev-server +++ b/bin/webpack-dev-server @@ -16,7 +16,8 @@ CONFIG_FILE = File.join(APP_PATH, "config/webpacker.yml") NODE_MODULES_PATH = File.join(APP_PATH, "node_modules") WEBPACK_CONFIG = File.join(APP_PATH, "config/webpack/#{NODE_ENV}.js") -DEFAULT_LISTEN_HOST_ADDR = NODE_ENV == "development" ? "localhost" : "0.0.0.0" +LOCALHOST = NODE_ENV == "development" || NODE_ENV == "localproduction" +DEFAULT_LISTEN_HOST_ADDR = LOCALHOST ? "localhost" : "0.0.0.0" def args(key) index = ARGV.index(key) diff --git a/case-study.md b/case-study.md new file mode 100644 index 00000000..50825406 --- /dev/null +++ b/case-study.md @@ -0,0 +1,105 @@ +# Case-study оптимизации + +## Актуальная проблема + +Нужно оптимизировать механизм загрузки главной страницы проекта `StoriesController#index`. + +Я решил исправить эту проблему, оптимизировав рендеринг шаблонов. + +## Формирование метрики + +Для того, чтобы понимать, дают ли мои изменения положительный эффект на быстродействие программы я придумал использовать такую метрику: число секунд, требуемые для прохождения нагрузочного бенчмарка `ab` с 30 запросами к главной странице проекта. + +## Вникаем в детали системы, чтобы найти главные точки роста + +Для того, чтобы найти "точки роста" для оптимизации я воспользовался инструментами rack-mini-profiler, Prometheus, Grafana. + +Вот какие проблемы удалось найти и решить + +### Ваша находка №1 + +- какой отчёт показал главную точку роста + + - rack-mini-profiler: заметное время занимает рендеринг `partial`-ов `_single_story.html.erb`, делаются 11 вызовов. Rendering: stories/\_main_stories_feed.html.erb duration 3.3ms, with children 57.1ms + - ``` + Document Path: / + Document Length: 139178 bytes + + Concurrency Level: 1 + Time taken for tests: 2.373 seconds + Complete requests: 30 + Failed requests: 0 + Total transferred: 4206630 bytes + HTML transferred: 4175340 bytes + Requests per second: 12.64 [#/sec] (mean) + Time per request: 79.097 [ms] (mean) + Time per request: 79.097 [ms] (mean, across all concurrent requests) + Transfer rate: 1731.23 [Kbytes/sec] received + + Connection Times (ms) + min mean[+/-sd] median max + Connect: 0 0 0.2 0 1 + Processing: 69 79 18.0 74 164 + Waiting: 69 79 18.0 74 164 + Total: 69 79 18.2 75 166 + + Percentage of the requests served within a certain time (ms) + 50% 75 + 66% 77 + 75% 79 + 80% 80 + 90% 89 + 95% 111 + 98% 166 + 99% 166 + 100% 166 (longest request) + ``` + + Нашей точкой роста будет число вызовов отдельных шаблонов `articles/_single_story.html.erb` + +- как вы решили её оптимизировать + - Применил кэширование в шаблоне при работе с коллекциями stories. Гипотеза состоит в том, что в случае не частых обновлений постов на главной странице кэширование снизит время изначальной загрузки за счет переиспользования данных об уже имеющихся постах. +- как изменилась метрика + + - Метрика снизилась с 2.37 сек до 1.42 сек + + ``` + Document Path: / + Document Length: Variable + + Concurrency Level: 1 + Time taken for tests: 1.423 seconds + Complete requests: 30 + Failed requests: 0 + Total transferred: 4208029 bytes + HTML transferred: 4176759 bytes + Requests per second: 21.08 [#/sec] (mean) + Time per request: 47.445 [ms] (mean) + Time per request: 47.445 [ms] (mean, across all concurrent requests) + Transfer rate: 2887.15 [Kbytes/sec] received + + Connection Times (ms) + min mean[+/-sd] median max + Connect: 0 0 0.2 0 1 + Processing: 39 47 15.7 44 120 + Waiting: 39 47 15.7 43 119 + Total: 39 47 15.9 44 121 + + Percentage of the requests served within a certain time (ms) + 50% 44 + 66% 45 + 75% 45 + 80% 47 + 90% 49 + 95% 84 + 98% 121 + 99% 121 + 100% 121 (longest request) + ``` + +- как изменился отчёт профилировщика + - Rendering: stories/\_main_stories_feed.html.erb duration 10.1ms, with children 10.2ms + +## Результаты + +В результате проделанной оптимизации удалось ускорить время рендеринга главной страницы более чем в полтора раза. diff --git a/config/application.rb b/config/application.rb index 1b4c391f..ae8a9fc7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -30,6 +30,7 @@ class Application < Rails::Application config.autoload_paths += Dir["#{config.root}/app/black_box/"] config.autoload_paths += Dir["#{config.root}/app/sanitizers"] config.autoload_paths += Dir["#{config.root}/lib/"] + config.autoload_paths += Dir["#{config.root}/app/metrics"] config.active_record.observers = :article_observer, :reaction_observer, :comment_observer config.active_job.queue_adapter = :delayed_job @@ -53,5 +54,7 @@ class Application < Rails::Application end ReservedWords.all = [ReservedWords::BASE_WORDS + top_routes].flatten.compact.uniq end + + config.skylight.environments << 'localproduction' if Rails.env.localproduction? end end diff --git a/config/boot.rb b/config/boot.rb index 28201161..245c324c 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,8 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +require "warning" +Gem.path.each do |path| + Warning.ignore(//, path) +end + require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/config/environments/development.rb b/config/environments/development.rb index ad3b97cb..a82d8a09 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -40,6 +40,7 @@ def yarn_integrity_enabled? # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log + $VERBOSE = nil # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load diff --git a/config/environments/localproduction.rb b/config/environments/localproduction.rb new file mode 100644 index 00000000..85ad0642 --- /dev/null +++ b/config/environments/localproduction.rb @@ -0,0 +1,107 @@ +# rubocop:disable Metrics/BlockLength + +Rails.application.configure do + # Verifies that versions and hashed value of the package contents in the project's package.json + config.webpacker.check_yarn_integrity = false + + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + "Cache-Control" => "public, max-age=172800" + } + + # Compress JavaScripts and CSS. + config.assets.js_compressor = Uglifier.new(harmony: true) + # config.assets.css_compressor = :sass + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = false + + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. + config.assets.digest = true + + # Supress logger output for asset requests. + config.assets.quiet = true + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = true + + # `config.assets.precompile` and `config.assets.version` + # have moved to config/initializers/assets.rb + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [:request_id] + + # Use a different logger for distributed setups. + # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true + + config.action_mailer.perform_caching = false + + config.app_domain = "localhost:3000" + + config.action_mailer.default_url_options = { host: "localhost:3000" } + config.action_mailer.delivery_method = :smtp + config.action_mailer.perform_deliveries = true + config.action_mailer.default_url_options = { host: config.app_domain } + config.action_mailer.smtp_settings = { + address: "smtp.gmail.com", + port: "587", + enable_starttls_auto: true, + user_name: '<%= ENV["DEVELOPMENT_EMAIL_USERNAME"] %>', + password: '<%= ENV["DEVELOPMENT_EMAIL_PASSWORD"] %>', + authentication: :plain, + domain: "localhost:3000" + } + + config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews" + + config.public_file_server.enabled = true + + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end + +# rubocop:enable Metrics/BlockLength diff --git a/config/environments/test.rb b/config/environments/test.rb index 85d96f7d..65ce2880 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -43,7 +43,8 @@ config.active_support.test_order = :random # Print deprecation notices to the stderr. - config.active_support.deprecation = :stderr + config.active_support.deprecation = :log + $VERBOSE = nil # Raises error for missing translations # config.action_view.raise_on_missing_translations = true @@ -51,5 +52,7 @@ # Install the Timber.io logger, but do not send logs. logger = Timber::Logger.new(nil) logger.level = config.log_level - config.logger = ActiveSupport::TaggedLogging.new(logger) + # config.logger = ActiveSupport::TaggedLogging.new(logger) + config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil)) + config.log_level = :fatal end diff --git a/config/initializers/airbrake.rb b/config/initializers/airbrake.rb index 0a3fffc7..6dd37705 100644 --- a/config/initializers/airbrake.rb +++ b/config/initializers/airbrake.rb @@ -41,7 +41,7 @@ # environments. # NOTE: This option *does not* work if you don't set the 'environment' option. # https://github.com/airbrake/airbrake-ruby#ignore_environments - c.ignore_environments = %w[test development] + c.ignore_environments = %w[test development localproduction] # A list of parameters that should be filtered out of what is sent to # Airbrake. By default, all "password" attributes will have their contents diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb index 9e191ac1..627333c9 100644 --- a/config/initializers/carrierwave.rb +++ b/config/initializers/carrierwave.rb @@ -3,7 +3,7 @@ require "carrierwave/storage/fog" CarrierWave.configure do |config| - if Rails.env.development? || Rails.env.test? + if Rails.env.development? || Rails.env.localproduction? || Rails.env.test? config.storage = :file else # config.fog_provider = 'fog-aws' diff --git a/config/initializers/honeycomb.rb b/config/initializers/honeycomb.rb index 0ac4787a..1ca2171c 100644 --- a/config/initializers/honeycomb.rb +++ b/config/initializers/honeycomb.rb @@ -1,21 +1,23 @@ -require "libhoney" +unless Rails.env.test? + require "libhoney" -key = ApplicationConfig["HONEYCOMB_API_KEY"] -dataset = "dev.to-#{Rails.env}" + key = ApplicationConfig["HONEYCOMB_API_KEY"] + dataset = "dev.to-#{Rails.env}" -$libhoney = if Rails.env.development? || Rails.env.test? - Libhoney::NullClient.new - else - Libhoney::Client.new( - writekey: key, - dataset: dataset, - user_agent_addition: HoneycombRails::USER_AGENT_SUFFIX, - ) - end + $libhoney = if Rails.env.development? || Rails.env.localproduction? + Libhoney::NullClient.new + else + Libhoney::Client.new( + writekey: key, + dataset: dataset, + user_agent_addition: HoneycombRails::USER_AGENT_SUFFIX, + ) + end -HoneycombRails.configure do |conf| - conf.writekey = key - conf.dataset = dataset - conf.db_dataset = "dev.to-db-#{Rails.env}" - conf.client = $libhoney + HoneycombRails.configure do |conf| + conf.writekey = key + conf.dataset = dataset + conf.db_dataset = "dev.to-db-#{Rails.env}" + conf.client = $libhoney + end end diff --git a/config/initializers/prometheus.rb b/config/initializers/prometheus.rb new file mode 100644 index 00000000..40e3a5b2 --- /dev/null +++ b/config/initializers/prometheus.rb @@ -0,0 +1,6 @@ +if Rails.env != "test" + require 'prometheus_exporter/middleware' + + # This reports stats per request like HTTP status and timings + Rails.application.middleware.unshift PrometheusExporter::Middleware +end diff --git a/config/initializers/rack_mini_profiler.rb b/config/initializers/rack_mini_profiler.rb new file mode 100644 index 00000000..c8349766 --- /dev/null +++ b/config/initializers/rack_mini_profiler.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +if Rails.env.development? || Rails.env.localproduction? + require "rack-mini-profiler" + + # The initializer was required late, so initialize it manually. + Rack::MiniProfilerRails.initialize!(Rails.application) + Rack::MiniProfiler.config.authorization_mode = :allow_all +end diff --git a/config/newrelic.yml b/config/newrelic.yml new file mode 100644 index 00000000..dfc90820 --- /dev/null +++ b/config/newrelic.yml @@ -0,0 +1,69 @@ +# +# This file configures the New Relic Agent. New Relic monitors Ruby, Java, +# .NET, PHP, Python, Node, and Go applications with deep visibility and low +# overhead. For more information, visit www.newrelic.com. +# +# Generated October 28, 2022 +# +# This configuration file is custom generated for NewRelic Administration +# +# For full documentation of agent configuration options, please refer to +# https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ruby-agent-configuration + +common: &default_settings + # Required license key associated with your New Relic account. + license_key: <%= ENV['NEW_RELIC_API_KEY'] %> + + # Your application name. Renaming here affects where data displays in New + # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications + app_name: 'DEV.to fork' + + distributed_tracing: + enabled: true + + # To disable the agent regardless of other settings, uncomment the following: + + # agent_enabled: false + + # Logging level for log/newrelic_agent.log + log_level: info + + application_logging: + # If `true`, all logging-related features for the agent can be enabled or disabled + # independently. If `false`, all logging-related features are disabled. + enabled: true + forwarding: + # If `true`, the agent captures log records emitted by this application. + enabled: true + # Defines the maximum number of log records to buffer in memory at a time. + max_samples_stored: 10000 + metrics: + # If `true`, the agent captures metrics related to logging for this application. + enabled: true + local_decorating: + # If `true`, the agent decorates logs with metadata to link to entities, hosts, traces, and spans. + # This requires a log forwarder to send your log files to New Relic. + # This should not be used when forwarding is enabled. + enabled: false + +# Environment-specific settings are in this section. +# RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment. +# If your application has other named environments, configure them here. +development: + <<: *default_settings + app_name: 'DEV.to fork (Development)' + +localproduction: + <<: *default_settings + app_name: 'DEV.to fork (Local Production)' + +test: + # It doesn't make sense to report to New Relic from automated test runs. + monitor_mode: false + +staging: + <<: *default_settings + app_name: 'DEV.to fork (Staging)' + +production: + <<: *default_settings diff --git a/config/secrets.yml b/config/secrets.yml index 73f5e05c..0bf176bb 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -13,6 +13,9 @@ development: secret_key_base: a60edc976c913b19fd9fc8118936fbe1df2b07f4eecc5ad32f975e33cd4ea36b150c1ce933b681b90874a46568041629003dcbfc07238f7dca91741bcd1ec870 +localproduction: + secret_key_base: a60edc976c913b19fd9fc8118936fbe1df2b07f4eecc5ad32f975e33cd4ea36b150c1ce933b681b90874a46568041629003dcbfc07238f7dca91741bcd1ec870 + test: secret_key_base: 42dd7834039ebbea271af22635a6782ee15e519b14629c5276bfcdd4cff841e9926994784bb43a335a8f8c9739bb254ea3afe831839d4dc65654ec7516ec25f0 diff --git a/config/webpack/localproduction.js b/config/webpack/localproduction.js new file mode 100644 index 00000000..d7346478 --- /dev/null +++ b/config/webpack/localproduction.js @@ -0,0 +1,3 @@ +const environment = require('./environment'); + +module.exports = environment.toWebpackConfig(); diff --git a/config/webpacker.yml b/config/webpacker.yml index 2dfcd170..e74e921d 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -39,6 +39,21 @@ development: hmr: false https: false +localproduction: + <<: *default + + # Production depends on precompilation of packs prior to booting for performance. + compile: false + + dev_server: + host: localhost + port: 3035 + hmr: false + https: false + + # Cache manifest.json for performance + cache_manifest: true + test: <<: *default compile: true diff --git a/lib/instrumentation.rb b/lib/instrumentation.rb index b43c9d79..ec4058b8 100644 --- a/lib/instrumentation.rb +++ b/lib/instrumentation.rb @@ -1,17 +1,22 @@ module Instrumentation def add_param_context(*keys) + return if Rails.env.test? + keys.each do |key| honeycomb_metadata[key] = params[key] end end def add_context(metadata) + return if Rails.env.test? + metadata.each do |key, value| honeycomb_metadata[key] = value end end def append_to_honeycomb(request, controller_name) + return if Rails.env.test? return if honeycomb_metadata.nil? honeycomb_metadata["trace.trace_id"] = request.request_id diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake new file mode 100644 index 00000000..922c26c5 --- /dev/null +++ b/lib/tasks/test.rake @@ -0,0 +1,30 @@ +namespace :test do + desc "run" + task run: :environment do + abort 'InfluxDB not running!' unless influx_running? + + # cmd = 'rspec' + cmd = "RUBYOPT='-W0 -W:no-experimental' bundle exec rake parallel:spec[3,'spec/(?!features)']" + puts "Running rspec via `#{cmd}`" + command = TTY::Command.new(printer: :quiet, color: true) + + start = Time.now + begin + command.run(cmd) + rescue TTY::Command::ExitError + puts 'TEST FAILED SAFELY' + end + finish = Time.now + + puts 'SENDING METRIC TO INFLUXDB' + TestDurationMetric.write(user: 'IgorArkhipov', run_time_seconds: (finish - start).to_i) + end + + def influx_running? + influx_endpoint = ENV['INFLUX_ENDPOINT'] || 'http://localhost:8086' + puts "Check InfluxDB on #{influx_endpoint}..." + + command = TTY::Command.new(printer: :null) + command.run!("curl #{influx_endpoint}/ping").success? + end +end diff --git a/prometheus/docker-compose.yml b/prometheus/docker-compose.yml new file mode 100644 index 00000000..7eb39649 --- /dev/null +++ b/prometheus/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3' +services: + dockerhost: + image: qoomon/docker-host + cap_add: [ 'NET_ADMIN', 'NET_RAW' ] + mem_limit: 8M + restart: on-failure + prometheus: + depends_on: [ dockerhost ] + image: prom/prometheus:latest + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=48h' + - '--web.enable-lifecycle' + restart: unless-stopped + ports: + - '9090:9090' # localhost:9090 + labels: + org.label-schema.group: "monitoring" + grafana: + image: grafana/grafana:latest + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=pass + depends_on: + - prometheus + ports: + - "3030:3000" # localhost:3030 dashboard, prometheus:9090 data source + volumes: + - grafana_data:/var/lib/grafana + - ./grafana/provisioning:/etc/grafana/provisioning + labels: + org.label-schema.group: "monitoring" +volumes: + prometheus_data: {} + grafana_data: {} diff --git a/prometheus/grafana/provisioning/dashboards/dashboard.yml b/prometheus/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000..4c387ae1 --- /dev/null +++ b/prometheus/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'Prometheus' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/prometheus/grafana/provisioning/dashboards/monitor_services.json b/prometheus/grafana/provisioning/dashboards/monitor_services.json new file mode 100644 index 00000000..ceac7dc9 --- /dev/null +++ b/prometheus/grafana/provisioning/dashboards/monitor_services.json @@ -0,0 +1,3023 @@ +{ + "id": null, + "title": "Monitor Services", + "tags": ["prometheus"], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": true, + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "decimals": 1, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 0 + }, + "hideTimeOverride": true, + "id": 1, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "s", + "postfixFontSize": "80%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "(time() - process_start_time_seconds{instance=\"localhost:9090\",job=\"prometheus\"})", + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": "", + "timeFrom": "10s", + "timeShift": null, + "title": "Prometheus Uptime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 0 + }, + "hideTimeOverride": true, + "id": 3, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "prometheus_tsdb_head_chunks", + "interval": "10s", + "intervalFactor": 1, + "refId": "A", + "step": 10 + } + ], + "thresholds": "", + "timeFrom": "10s", + "timeShift": null, + "title": "In-Memory Chunks", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 18, + "y": 0 + }, + "hideTimeOverride": true, + "id": 2, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "prometheus_tsdb_head_series", + "interval": "10s", + "intervalFactor": 1, + "refId": "A", + "step": 10 + } + ], + "thresholds": "", + "timeFrom": "10s", + "timeShift": null, + "title": "In-Memory Series", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 22, + "panels": [], + "repeat": null, + "title": "Prometheus Metrics", + "type": "row" + }, + { + "aliasColors": { + "Max": "#e24d42", + "Open": "#508642" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 25 + }, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_max_fds{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Max", + "refId": "A" + }, + { + "expr": "process_open_fds{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Open", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "File Descriptors", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Allocated bytes": "#7EB26D", + "Allocated bytes - 1m max": "#BF1B00", + "Allocated bytes - 1m min": "#BF1B00", + "Allocated bytes - 5m max": "#BF1B00", + "Allocated bytes - 5m min": "#BF1B00", + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "RSS": "#447EBC" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 25 + }, + "id": 58, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/-/", + "fill": 0 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_resident_memory_bytes{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "RSS", + "metric": "process_resident_memory_bytes", + "refId": "B", + "step": 10 + }, + { + "expr": "prometheus_local_storage_target_heap_size_bytes{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Target heap size", + "metric": "go_memstats_alloc_bytes", + "refId": "D", + "step": 10 + }, + { + "expr": "go_memstats_next_gc_bytes{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Next GC", + "metric": "go_memstats_next_gc_bytes", + "refId": "C", + "step": 10 + }, + { + "expr": "go_memstats_alloc_bytes{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Allocated", + "metric": "go_memstats_alloc_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Allocated bytes": "#F9BA8F", + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "RSS": "#890F02" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 25 + }, + "id": 60, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(go_memstats_alloc_bytes_total{job=\"prometheus\"}[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Allocated", + "metric": "go_memstats_alloc_bytes", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Allocations", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "Time series": "#70dbed" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 32 + }, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_tsdb_head_series", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Time series", + "metric": "prometheus_local_storage_memory_series", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Time series", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 32 + }, + "id": 24, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_tsdb_head_active_appenders", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Head Appenders", + "metric": "prometheus_local_storage_memory_series", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Active Appenders", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "samples/s": "#e5a8e2" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 32 + }, + "id": 26, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_head_samples_appended_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Samples", + "metric": "prometheus_local_storage_ingested_samples_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Samples Appended", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "To persist": "#9AC48A" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Max.*/", + "fill": 0 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_tsdb_head_chunks", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Chunks", + "metric": "prometheus_local_storage_memory_chunks", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Chunks", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 30, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_head_chunks_created_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Created", + "metric": "prometheus_local_storage_chunk_ops_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Chunks Created", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "Removed": "#e5ac0e" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_head_chunks_removed_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Removed", + "metric": "prometheus_local_storage_chunk_ops_total", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Chunks Removed", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max": "#447ebc", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "Min": "#447ebc", + "Now": "#7eb26d" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 46 + }, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "Max", + "fillBelowTo": "Min", + "lines": false + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_tsdb_head_min_time", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Min", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + }, + { + "expr": "time() * 1000", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "Now", + "refId": "C" + }, + { + "expr": "prometheus_tsdb_head_max_time", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Max", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head Time Range", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "dateTimeAsIso", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 46 + }, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_head_gc_duration_seconds_sum[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "GC Time", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Head GC Time/s", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 46 + }, + "id": 38, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "Queue length", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_tsdb_blocks_loaded", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Blocks Loaded", + "metric": "prometheus_local_storage_indexing_batch_sizes_sum", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Blocks Loaded", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Failed Compactions": "#bf1b00", + "Failed Reloads": "#bf1b00", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 53 + }, + "id": 40, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_reloads_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Reloads", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TSDB Reloads", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Failed Compactions": "#bf1b00", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "{instance=\"demo.robustperception.io:9090\",job=\"prometheus\"}": "#bf1b00" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 53 + }, + "id": 44, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_wal_corruptions_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "WAL Corruptions", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + }, + { + "expr": "rate(prometheus_tsdb_reloads_failures_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Reload Failures", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "B", + "step": 10 + }, + { + "expr": "rate(prometheus_tsdb_head_series_not_found[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Head Series Not Found", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "C", + "step": 10 + }, + { + "expr": "rate(prometheus_tsdb_compactions_failed_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Compaction Failures", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "D", + "step": 10 + }, + { + "expr": "rate(prometheus_tsdb_retention_cutoffs_failures_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Retention Cutoff Failures", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "E", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "TSDB Problems", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Failed Compactions": "#bf1b00", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 53 + }, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_wal_fsync_duration_seconds_sum[1m]) / rate(prometheus_tsdb_wal_fsync_duration_seconds_count[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Fsync Latency", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + }, + { + "expr": "rate(prometheus_tsdb_wal_truncate_duration_seconds_sum[1m]) / rate(prometheus_tsdb_wal_truncate_duration_seconds_count[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Truncate Latency", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "B", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "WAL Latencies", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Failed Compactions": "#bf1b00", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 60 + }, + "id": 46, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_compactions_total[10m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Compactions", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compactions", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 60 + }, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_compaction_duration_seconds_sum[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Compaction Time", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Compaction Time", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Allocated bytes": "#F9BA8F", + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833", + "RSS": "#890F02" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 60 + }, + "id": 50, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_retention_cutoffs_total[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Retention Cutoffs", + "metric": "last", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Retention Cutoffs", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 67 + }, + "id": 56, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_tsdb_compaction_chunk_samples_sum[10m]) / rate(prometheus_tsdb_compaction_chunk_samples_count[10m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Chunk Samples", + "metric": "prometheus_local_storage_series_chunks_persisted_count", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "First Compaction, Avg Chunk Samples", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 67 + }, + "id": 10, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_target_interval_length_seconds_count[5m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ interval }}", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Target Scrapes", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 67 + }, + "id": 11, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "prometheus_target_interval_length_seconds{quantile!=\"0.01\", quantile!=\"0.05\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{quantile}} ({{interval}})", + "refId": "A", + "step": 2 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Scrape Duration", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 74 + }, + "id": 62, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_http_request_duration_seconds_count[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP requests", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 74 + }, + "id": 64, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_http_request_duration_seconds_sum[1m]) / rate(prometheus_http_request_duration_seconds_count[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP request latency", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 74 + }, + "id": 66, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_http_request_duration_seconds_sum[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Time spent in HTTP requests", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "Time spent in each mode, per second", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 81 + }, + "id": 68, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_engine_query_duration_seconds_sum[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{slice}}", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Query engine timings", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 81 + }, + "id": 70, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_rule_group_iterations_missed_total[1m]) ", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Rule group missed", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "B", + "step": 10 + }, + { + "expr": "rate(prometheus_rule_evaluation_failures_total[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Rule evals failed", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "C", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Rule group evaulation problems", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Chunks": "#1F78C1", + "Chunks to persist": "#508642", + "Max chunks": "#052B51", + "Max to persist": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 81 + }, + "id": 72, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(prometheus_rule_group_duration_seconds_sum[1m])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Rule evaluation duration", + "metric": "prometheus_local_storage_memory_chunkdescs", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Evaluation time of rule groups", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "10s", + "schemaVersion": 12, + "version": 22, + "links": [], + "gnetId": null +} diff --git a/prometheus/grafana/provisioning/datasources/datasource.yml b/prometheus/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 00000000..bb37f13d --- /dev/null +++ b/prometheus/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9090 + basicAuth: false + isDefault: true + editable: true \ No newline at end of file diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml new file mode 100644 index 00000000..cb40481a --- /dev/null +++ b/prometheus/prometheus.yml @@ -0,0 +1,13 @@ +global: + scrape_interval: 5s + external_labels: + monitor: 'local-monitor' +scrape_configs: + - job_name: 'prometheus' + honor_labels: true + static_configs: + - targets: ["localhost:9090"] + - job_name: 'devto' + honor_labels: true + static_configs: + - targets: ['dockerhost:9394'] diff --git a/spec/controllers/internal_users_controller_spec.rb b/spec/controllers/internal_users_controller_spec.rb index de122d3b..c6a2bc9b 100644 --- a/spec/controllers/internal_users_controller_spec.rb +++ b/spec/controllers/internal_users_controller_spec.rb @@ -1,18 +1,18 @@ require "rails_helper" RSpec.describe "internal/users", type: :request do - let(:user) { create(:user) } - let(:user2) { create(:user) } - let(:user3) { create(:user) } - let(:super_admin) { create(:user, :super_admin) } - let(:article) { create(:article, user: user) } - let(:article2) { create(:article, user: user2) } + let_it_be(:user) { create(:user) } + let_it_be(:user2) { create(:user) } + let_it_be(:user3) { create(:user) } + let_it_be(:super_admin) { create(:user, :super_admin) } + let_it_be(:article) { create(:article, user: user) } + let_it_be(:article2) { create(:article, user: user2) } before do sign_in super_admin - user - user2 - Delayed::Worker.new(quiet: true).work_off + end + + before_all do dependents_for_offending_user_article offender_activity_on_other_content end @@ -28,7 +28,6 @@ def dependents_for_offending_user_article create(:reaction, reactable: comment2, reactable_type: "Comment", user: user2) # create user3 reaction to offending article create(:reaction, reactable: article, reactable_type: "Article", user: user3, category: "like") - Delayed::Worker.new(quiet: true).work_off end def offender_activity_on_other_content @@ -38,7 +37,6 @@ def offender_activity_on_other_content comment = create(:comment, commentable_type: "Article", commentable: article2, user: user) # user3 reacts to offender comment create(:reaction, reactable: comment, reactable_type: "Comment", user: user3) - Delayed::Worker.new(quiet: true).work_off end context "when deleting user" do @@ -62,7 +60,6 @@ def create_mutual_follows create_mutual_follows create_mention create(:badge_achievement, rewarder_id: 1, rewarding_context_message: "yay", user_id: user.id) - Delayed::Worker.new(quiet: true).work_off end it "raises a 'record not found' error after deletion" do @@ -90,7 +87,6 @@ def create_mutual_follows context "when banishing user" do def banish_user post "/internal/users/#{user.id}/banish" - Delayed::Worker.new(quiet: true).work_off user.reload end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 18bf58d8..7e2f55ff 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -12,7 +12,7 @@ name { Faker::Name.name } email { generate :email } username { generate :username } - profile_image { image } + # profile_image { image } twitter_username { generate :twitter_username } github_username { generate :github_username } summary { Faker::Lorem.paragraph[0..rand(190)] } diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index d21359db..f162ce54 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -8,7 +8,7 @@ def build_and_validate_article(*args) article end - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } let(:article) { create(:article, user_id: user.id) } it { is_expected.to validate_uniqueness_of(:canonical_url).allow_blank } @@ -40,7 +40,7 @@ def build_and_validate_article(*args) end it "reject future dates" do - expect(build(:article, with_date: true, date: "01/01/2020").valid?).to be(false) + expect(build(:article, with_date: true, date: DateTime.now + 1.day).valid?).to be(false) end it "has proper username" do @@ -107,7 +107,7 @@ def build_and_validate_article(*args) end describe "#slug" do - let(:title) { "hey This' is$ a SLUG" } + let_it_be(:title) { "hey This' is$ a SLUG" } let(:article0) { build(:article, title: title, published: false) } let(:article1) { build(:article, title: title, published: false) } @@ -142,8 +142,8 @@ def build_and_validate_article(*args) context "when provided with body_markdown" do let(:test_article) { build(:article, title: title) } - let(:title) { "Talk About It, Justify It" } - let(:slug) { "talk-about-it-justify-it" } + let_it_be(:title) { "Talk About It, Justify It" } + let_it_be(:slug) { "talk-about-it-justify-it" } before { test_article.validate } @@ -175,7 +175,7 @@ def build_and_validate_article(*args) end describe "#canonical_url" do - let(:article_with_canon_url) { build(:article, with_canonical_url: true) } + let_it_be(:article_with_canon_url) { build(:article, with_canonical_url: true) } before do article_with_canon_url.validate @@ -314,7 +314,7 @@ def build_and_validate_article(*args) end it "removes from search index" do - article = create(:article) + article = build_stubbed(:article) article.remove_algolia_index end @@ -351,9 +351,7 @@ def build_and_validate_article(*args) end describe "::filter_excluded_tags" do - before do - create(:article, tags: "hiring") - end + let_it_be(:article) { create(:article, tags: "hiring") } it "exlude #hiring when no argument is given" do expect(described_class.filter_excluded_tags.length).to be(0) @@ -379,7 +377,7 @@ def build_and_validate_article(*args) expect(article.path).to eq("/#{article.username}/#{article.slug}") end it "assigns cached_user_name on save" do - article = create(:article, user_id: user.id) + article = build_stubbed(:article, user_id: user.id) expect(article.cached_user_name).to eq(article.cached_user_name) end it "assigns cached_user_username on save" do @@ -431,7 +429,7 @@ def build_and_validate_article(*args) end context "when unpublished" do - let(:article) { create(:article, published: false) } + let_it_be(:article) { create(:article, published: false) } it "does not update the hotness score" do article.save diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index d0fd7575..f5cdea04 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -1,14 +1,14 @@ require "rails_helper" RSpec.describe Comment, type: :model do - let(:user) { create(:user, created_at: 3.weeks.ago) } - let(:user2) { create(:user) } - let(:article) { create(:article, user_id: user.id, published: true) } - let(:article_with_video) { create(:article, :video, user_id: user.id, published: true) } - let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id) } - let(:video_comment) { create(:comment, user_id: user2.id, commentable_id: article_with_video.id) } - let(:comment_2) { create(:comment, user_id: user2.id, commentable_id: article.id) } - let(:child_comment) do + let_it_be(:user) { create(:user, created_at: 3.weeks.ago) } + let_it_be(:user2) { create(:user) } + let_it_be(:article) { create(:article, user_id: user.id, published: true) } + let_it_be(:article_with_video) { create(:article, :video, user_id: user.id, published: true) } + let_it_be(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id) } + let_it_be(:video_comment) { create(:comment, user_id: user2.id, commentable_id: article_with_video.id) } + let_it_be(:comment_2) { create(:comment, user_id: user2.id, commentable_id: article.id) } + let_it_be(:child_comment) do build(:comment, user_id: user.id, commentable_id: article.id, parent_id: comment.id) end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 2877fcd0..24512dc8 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -3,8 +3,8 @@ RSpec.describe Notification, type: :model do let(:user) { create(:user) } let(:user2) { create(:user) } - let(:user3) { create(:user) } - let(:organization) { create(:organization) } + let_it_be(:user3) { create(:user) } + let_it_be(:organization) { create(:organization) } let(:article) { create(:article, user_id: user.id, page_views_count: 4000, positive_reactions_count: 70) } let(:follow_instance) { user.follow(user2) } @@ -86,19 +86,19 @@ end it "does not send a notification to the author of the article if the commenter is the author" do - comment = create(:comment, user: user, commentable: article) + comment = build_stubbed(:comment, user: user, commentable: article) Notification.send_new_comment_notifications_without_delay(comment) expect(user.notifications.count).to eq 0 end it "does not send a notification to the author of the comment" do - comment = create(:comment, user: user2, commentable: article) + comment = build_stubbed(:comment, user: user2, commentable: article) Notification.send_new_comment_notifications_without_delay(comment) expect(user2.notifications.count).to eq 0 end it "sends a notification to the organization" do - org = create(:organization) + org = build_stubbed(:organization) user.update(organization: org) article.update(organization: org) comment = create(:comment, user: user2, commentable: article) @@ -110,7 +110,7 @@ context "when the author of the article is not subscribed" do it "does not send a notification to the author of the article" do article.update(receive_notifications: false) - comment = create(:comment, user: user2, commentable: article) + comment = build_stubbed(:comment, user: user2, commentable: article) Notification.send_new_comment_notifications_without_delay(comment) expect(user.notifications.count).to eq 0 end @@ -120,7 +120,7 @@ it "does not send a notification to the author of the comment" do parent_comment = create(:comment, user: user2, commentable: article) parent_comment.update(receive_notifications: false) - child_comment = create(:comment, user: user, commentable: article, ancestry: parent_comment.id.to_s) + child_comment = build_stubbed(:comment, user: user, commentable: article, ancestry: parent_comment.id.to_s) Notification.send_new_comment_notifications_without_delay(child_comment) expect(user2.notifications.count).to eq 0 end @@ -147,14 +147,14 @@ it "does not send a notification to the author of a comment" do comment = create(:comment, user: user2, commentable: article) comment.update(receive_notifications: false) - reaction = create(:reaction, reactable: comment, user: user) + reaction = build_stubbed(:reaction, reactable: comment, user: user) Notification.send_reaction_notification_without_delay(reaction, reaction.reactable.user) expect(user2.notifications.count).to eq 0 end it "does not send a notification to the author of an article" do article.update(receive_notifications: false) - reaction = create(:reaction, reactable: article, user: user2) + reaction = build_stubbed(:reaction, reactable: article, user: user2) Notification.send_reaction_notification_without_delay(reaction, reaction.reactable.user) expect(user.notifications.count).to eq 0 end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 1db91653..7ae9d078 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -12,6 +12,8 @@ require "pundit/rspec" require "webmock/rspec" require "test_prof/recipes/rspec/before_all" +require "test_prof/recipes/rspec/let_it_be" +require "test_prof/recipes/rspec/sample" # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -37,9 +39,15 @@ # Disable internet connection with Webmock WebMock.disable_net_connect!(allow_localhost: true) +TestProf::StackProf.configure do |config| + config.format = 'json' +end + RSpec.configure do |config| config.fixture_path = "#{::Rails.root}/spec/fixtures" + config.use_transactional_fixtures = true + config.include ApplicationHelper config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :view @@ -83,6 +91,9 @@ stub_request(:any, /api.mailchimp.com/). to_return(status: 200, body: "", headers: {}) + + stub_request(:post, /www.google-analytics.com/). + to_return(status: 200, body: "", headers: {}) end OmniAuth.config.test_mode = true diff --git a/spec/requests/notifications_spec.rb b/spec/requests/notifications_spec.rb index 4ceab059..21804a12 100644 --- a/spec/requests/notifications_spec.rb +++ b/spec/requests/notifications_spec.rb @@ -1,8 +1,8 @@ require "rails_helper" RSpec.describe "NotificationsIndex", type: :request do - let(:dev_account) { create(:user) } - let(:user) { create(:user) } + let_it_be(:dev_account) { create(:user) } + let_it_be(:user) { create(:user) } before do allow(User).to receive(:dev_account).and_return(dev_account) @@ -71,9 +71,9 @@ def mock_follow_notifications(amount) end context "when a user has new reaction notifications" do - let(:article1) { create(:article, user_id: user.id) } - let(:article2) { create(:article, user_id: user.id) } - let(:special_characters_article) { create(:article, user_id: user.id, title: "What's Become of Waring") } + let_it_be(:article1) { create(:article, user_id: user.id) } + let_it_be(:article2) { create(:article, user_id: user.id) } + let_it_be(:special_characters_article) { create(:article, user_id: user.id, title: "What's Become of Waring") } before { sign_in user } @@ -141,29 +141,32 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end context "when a user has a new comment notification" do - let(:user2) { create(:user) } - let(:article) { create(:article, user_id: user.id) } - let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") } + let_it_be(:user2) { create(:user) } + let_it_be(:article) { create(:article, user_id: user.id) } + let_it_be(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") } before do sign_in user Notification.send_new_comment_notifications_without_delay(comment) - get "/notifications" end it "renders the correct message" do + get "/notifications" expect(response.body).to include "commented on" end it "does not render the moderation message" do + get "/notifications" expect(response.body).not_to include "As a trusted member" end it "renders the article's path" do + get "/notifications" expect(response.body).to include article.path end it "renders the comment's processed HTML" do + get "/notifications" expect(response.body).to include comment.processed_html end @@ -174,22 +177,26 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end it "does not render the reaction as reacted if it was not reacted on" do + get "/notifications" expect(response.body).not_to include "reaction-button reacted" end end context "when a user has a new moderation notification" do - let(:user2) { create(:user) } - let(:article) { create(:article, user_id: user.id) } - let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") } + let_it_be(:user2) { create(:user) } + let_it_be(:article) { create(:article, user_id: user.id) } + let_it_be(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") } before do - user.add_role :trusted sign_in user Notification.send_moderation_notification_without_delay(comment) get "/notifications" end + before_all do + user.add_role :trusted + end + it "renders the proper message" do expect(response.body).to include "As a trusted member" end @@ -204,23 +211,25 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end context "when a user has a new welcome notification" do + let_it_be(:broadcast) { create(:broadcast, :onboarding) } + before do sign_in user + Notification.send_welcome_notification_without_delay(user.id) + get "/notifications" end it "renders the welcome notification" do - broadcast = create(:broadcast, :onboarding) - Notification.send_welcome_notification_without_delay(user.id) - get "/notifications" expect(response.body).to include broadcast.processed_html end end context "when a user has a new badge notification" do + let_it_be(:badge) { create(:badge) } + let_it_be(:badge_achievement) { create(:badge_achievement, user: user, badge: badge) } + before do sign_in user - badge = create(:badge) - badge_achievement = create(:badge_achievement, user: user, badge: badge) Notification.send_new_badge_notification_without_delay(badge_achievement) get "/notifications" end @@ -243,9 +252,9 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end context "when a user has a new mention notification" do - let(:user2) { create(:user) } - let(:article) { create(:article, user_id: user.id) } - let(:comment) do + let_it_be(:user2) { create(:user) } + let_it_be(:article) { create(:article, user_id: user.id) } + let_it_be(:comment) do create( :comment, user_id: user2.id, @@ -256,13 +265,15 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end before do - comment - Mention.create_all_without_delay(comment) - Notification.send_mention_notification_without_delay(Mention.first) sign_in user + Notification.send_mention_notification_without_delay(Mention.first) get "/notifications" end + before_all do + Mention.create_all_without_delay(comment) + end + it "renders the proper message" do expect(response.body).to include "mentioned you in a comment" end @@ -273,16 +284,19 @@ def mock_heart_reaction_notifications(amount, categories, reactable = article1) end context "when a user has a new article notification" do - let(:user2) { create(:user) } - let(:article) { create(:article, user_id: user.id) } + let_it_be(:user2) { create(:user) } + let_it_be(:article) { create(:article, user_id: user.id) } before do - user2.follow(user) - Notification.send_to_followers_without_delay(article, "Published") sign_in user2 + Notification.send_to_followers_without_delay(article, "Published") get "/notifications" end + before_all do + user2.follow(user) + end + it "renders the proper message" do expect(response.body).to include "made a new post:" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3c1db9da..3446e5f3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,4 @@ -require "simplecov" +# require "simplecov" require "zonebie/rspec" # This file was generated by the `rails generate rspec:install` command. Conventionally, all @@ -104,4 +104,5 @@ # # test failures related to randomization by passing the same `--seed` value # # as the one that triggered the failure. # Kernel.srand config.seed + config.exclude_pattern = 'spec/features/**/*_spec.rb' unless ARGV.detect { _1 =~ /^spec/ } =~ /^spec\/features/ end diff --git a/spec/support/initializers/capybara.rb b/spec/support/initializers/capybara.rb index 37555652..355ae9d3 100644 --- a/spec/support/initializers/capybara.rb +++ b/spec/support/initializers/capybara.rb @@ -16,4 +16,4 @@ ) end -Capybara.javascript_driver = :headless_chrome +Capybara.javascript_driver = :selenium_chrome_headless diff --git a/spec/support/initializers/database_cleaner.rb b/spec/support/initializers/database_cleaner.rb index dc109b60..3a8df717 100644 --- a/spec/support/initializers/database_cleaner.rb +++ b/spec/support/initializers/database_cleaner.rb @@ -1,22 +1,11 @@ require "database_cleaner" RSpec.configure do |config| + return if config.use_transactional_fixtures? + config.use_transactional_fixtures = false config.before(:suite) do - if config.use_transactional_fixtures? - raise(<<-MSG) - Delete line `config.use_transactional_fixtures = true` from rails_helper.rb - (or set it to false) to prevent uncommitted transactions being used in - JavaScript-dependent specs. - - During testing, the app-under-test that the browser driver connects to - uses a different database connection to the database connection used by - the spec. The app's database connection would not be able to access - uncommitted transaction data setup over the spec's database connection. - MSG - end - DatabaseCleaner.clean_with(:truncation) end