Violet Rails: The Restarone Way - restarone/violet_rails GitHub Wiki

Monitoring and Telemetry

We use monitoring and telemetry to catch errors and triage, contributing to the general operational excellence of an organization.

Appointing system guardians

It is recommended you appoint an individual or team to receive Violet Rails error traces by registering them like so:

Dissecting an error trace

Once registered, they will receive error messages via email as they happen. A sample may look something like this:

restarone.solutionsgraphql#execute (ActiveRecord::StatementInvalid) "PG::UndefinedColumn: ERROR: column "time" does n...

An ActiveRecord::StatementInvalid occurred in graphql#execute:

  PG::UndefinedColumn: ERROR:  column "time" does not exist
LINE 1: ...ELECT "ahoy_visits".* FROM "ahoy_visits" ORDER BY time desc ...
                                                             ^




-------------------------------
Request:
-------------------------------

  * URL        : https://www.restarone.solutions/graphql
  * HTTP Method: POST
  * IP address : 142.181.192.212
  * Parameters : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "controller"=>"graphql", "action"=>"execute", "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil}}
  * Timestamp  : 2022-06-23 12:11:50 UTC
  * Server : ip-172-31-86-57
    * Rails root : /var/www/violet/releases/20220623114044
  * Process: 2839150

-------------------------------
Session:
-------------------------------

  * session id: [FILTERED]
  * data: {"session_id"=>"4a19ecf6020e7c0feb26fa23c9c102ef",
   "user_return_to"=>"/admin",
   "warden.user.user.key"=>[[1], "$2a$12$89B5jU.44t5BXSkZNPRJqu"],
   "warden.user.user.session"=>{"last_request_at"=>1655986310},
   "site_id"=>1,
   "_csrf_token"=>"sSPxmpKSVLNWc7QvjywHrvkJWSLQpGBMHJg3zeqIZVg="}

-------------------------------
Environment:
-------------------------------

  * CONTENT_LENGTH                                          : 169
    * CONTENT_TYPE                                            : application/json
    * GATEWAY_INTERFACE                                       : CGI/1.2
    * HTTP_ACCEPT                                             : */*
    * HTTP_ACCEPT_ENCODING                                    : gzip, deflate, br
    * HTTP_ACCEPT_LANGUAGE                                    : en-CA,en-US;q=0.9,en;q=0.8
    * HTTP_CONNECTION                                         : close
    * HTTP_COOKIE                                             : _r_solutions_session=WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d%2FZz%2BXMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf%2BQtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt%2BE1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg%2F3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmVBawLlF9sDy1ehLc%2BTM0ynRNy6WTxd09Kuj%2Brc20%2Bepjy8NHk3a3i8Ab4vrPg6U1mztiHbjXhTg1ZvDCmM0ZBBiTmOdHAs5irSZcdpAGgwFa7SI8x%2BBTdS6CP6VW9l6pdJAgSXUwbL%2BXBk7JLX5k8qFMb6y4EVneyH%2BLFpHwgY8W0hiM1%2BYzhrAlp933nrFroeE5ng8K3Dy1368VICWSk5wmC9D31gL4rCawHcECu%2Fgr%2Bo5Qb2TS%2BfizPGavbN%2FNFUk2szjug0wP%2FbyxthHDrj9TSPYDTdKnjVBOIQB0dE%3D--H8TqpUcF6nQid9XH--EcvI8XO4sXCNdAepxxU0EQ%3D%3D; ahoy_visit=5baffb58-186f-4fb7-8e0a-b855245a9432; ahoy_visitor=1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e
    * HTTP_HOST                                               : www.restarone.solutions
    * HTTP_ORIGIN                                             : https://www.restarone.solutions/
    * HTTP_REFERER                                            : https://www.restarone.solutions/admin/graphiql
    * HTTP_USER_AGENT                                         : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15
    * HTTP_VERSION                                            : HTTP/1.0
    * HTTP_X_CSRF_TOKEN                                       : [FILTERED]
    * HTTP_X_FORWARDED_FOR                                    : 142.181.192.212
    * HTTP_X_FORWARDED_PROTO                                  : https
    * HTTP_X_REAL_IP                                          : 142.181.192.212
    * ORIGINAL_FULLPATH                                       : /graphql
    * ORIGINAL_SCRIPT_NAME                                    :
    * PATH_INFO                                               : /graphql
    * QUERY_STRING                                            :
    * RAW_POST_DATA                                           : [FILTERED]
    * REMOTE_ADDR                                             : 127.0.0.1
    * REQUEST_METHOD                                          : POST
    * REQUEST_PATH                                            : /graphql
    * REQUEST_URI                                             : /graphql
    * ROUTES_47032437798660_SCRIPT_NAME                       :
    * SCRIPT_NAME                                             :
    * SERVER_NAME                                             : www.restarone.solutions
    * SERVER_PORT                                             : 443
    * SERVER_PROTOCOL                                         : HTTP/1.1
    * SERVER_SOFTWARE                                         : puma 5.6.4 Birdie's Version
    * action_controller.instance                              : #<GraphqlController:0x00007fd03c34f260>
    * action_dispatch.authenticated_encrypted_cookie_salt     : [FILTERED]
    * action_dispatch.backtrace_cleaner                       : #<Rails::BacktraceCleaner:0x0000558d2c51e618>
    * action_dispatch.content_security_policy                 :
    * action_dispatch.content_security_policy_nonce_directives:
    * action_dispatch.content_security_policy_nonce_generator :
    * action_dispatch.content_security_policy_report_only     : false
    * action_dispatch.cookies                                 : #<ActionDispatch::Cookies::CookieJar:0x00007fd03c4dacd8>
    * action_dispatch.cookies_digest                          :
    * action_dispatch.cookies_rotations                       : #<ActiveSupport::Messages::RotationConfiguration:0x0000558d2b244c40>
    * action_dispatch.cookies_same_site_protection            : #<Proc:0x0000558d2e735828@/var/www/violet/shared/bundle/ruby/2.6.0/gems/railties-6.1.5/lib/rails/application.rb:636>
    * action_dispatch.cookies_serializer                      : json
    * action_dispatch.encrypted_cookie_cipher                 : [FILTERED]
    * action_dispatch.encrypted_cookie_salt                   : [FILTERED]
    * action_dispatch.encrypted_signed_cookie_salt            : [FILTERED]
    * action_dispatch.http_auth_salt                          : [FILTERED]
    * action_dispatch.key_generator                           : #<ActiveSupport::CachingKeyGenerator:0x0000558d2f160dc0>
    * action_dispatch.logger                                  : #<ActiveSupport::Logger:0x0000558d2c7c7db0>
    * action_dispatch.parameter_filter                        : [:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn]
    * action_dispatch.permissions_policy                      :
    * action_dispatch.redirect_filter                         : []
    * action_dispatch.remote_ip                               : 142.181.192.212
    * action_dispatch.request.accepts                         : [#<Mime::Type:0x00007fd03c3530b8 @synonyms=[], @symbol=nil, @string="*/*", @hash=-3532471350324634667>]
    * action_dispatch.request.content_type                    : application/json
    * action_dispatch.request.formats                         : [#<Mime::Type:0x00007fd03c3530b8 @synonyms=[], @symbol=nil, @string="*/*", @hash=-3532471350324634667>]
    * action_dispatch.request.parameters                      : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "controller"=>"graphql", "action"=>"execute", "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDi...
    * action_dispatch.request.path_parameters                 : {:controller=>"graphql", :action=>"execute"}
    * action_dispatch.request.query_parameters                : {}
    * action_dispatch.request.request_parameters              : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n   ...
    * action_dispatch.request.unsigned_session_cookie         : {"session_id"=>"4a19ecf6020e7c0feb26fa23c9c102ef", "user_return_to"=>"/admin", "warden.user.user.key"=>[[1], "$2a$12$89B5jU.44t5BXSkZNPRJqu"], "warden.user.user.session"=>{"last_request_at"=>1655986310}, "site_id"=>1, "_csrf_token"=>"[FILTERED]"}
    * action_dispatch.request_id                              : 697ce9b3-2407-41a0-9dd6-3b3c553bc921
    * action_dispatch.routes                                  : #<ActionDispatch::Routing::RouteSet:0x0000558d2f7eee08>
    * action_dispatch.secret_key_base                         : [FILTERED]
    * action_dispatch.show_detailed_exceptions                : false
    * action_dispatch.show_exceptions                         : true
    * action_dispatch.signed_cookie_digest                    :
    * action_dispatch.signed_cookie_salt                      : [FILTERED]
    * action_dispatch.use_authenticated_cookie_encryption     : [FILTERED]
    * action_dispatch.use_cookies_with_metadata               : true
    * exception_notifier.exception_data                       : {:current_user=>#<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "contact@restaron...
    * puma.config                                             : #<Puma::Configuration:0x0000558d29a312e8>
    * puma.request_body_wait                                  : 0
    * puma.socket                                             : #<TCPSocket:0x00007fd03c305db8>
    * rack.after_reply                                        : []
    * rack.cors                                               : #<Rack::Cors::Result:0x00007fd03c3051d8>
    * rack.errors                                             : #<IO:0x0000558d28b580c8>
    * rack.hijack                                             : #<Puma::Client:0x00007fd03c305d40>
    * rack.hijack?                                            : true
    * rack.input                                              : #<StringIO:0x00007fd03c3056d8>
    * rack.multiprocess                                       : false
    * rack.multithread                                        : true
    * rack.request.cookie_hash                                : {"_r_solutions_session"=>"WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d/Zz+XMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf+QtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt+E1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg/3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmV...
    * rack.request.cookie_string                              : _r_solutions_session=WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d%2FZz%2BXMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf%2BQtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt%2BE1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg%2F3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmVBawLlF9sDy1ehLc%2BTM0ynRNy6WTxd09Kuj%2Brc20%2Bepjy8NHk3a3i8Ab4vrPg6U1mztiHbjXhTg1ZvDCmM0ZBBiTmOdHAs5irSZcdpAGgwFa7SI8x%2BBTdS6CP6VW9l6pdJAgSXUwbL%2BXBk7JLX5k8qFMb6y4EVneyH%2BLFpHwgY8W0hiM1%2BYzhrAlp933nrFroeE5ng8K3Dy1368VICWSk5wmC9D31gL4rCawHcECu%2Fgr%2Bo5Qb2TS%2BfizPGavbN%2FNFUk2szjug0wP%2FbyxthHDrj9TSPYDTdKnjVBOIQB0dE%3D--H8TqpUcF6nQid9XH--EcvI8XO4sXCNdAepxxU0EQ%3D%3D; ahoy_visit=5baffb58-186f-4fb7-8e0a-b855245a9432; ahoy_visitor=1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e
    * rack.request.query_hash                                 : {}
    * rack.request.query_string                               :
    * rack.run_once                                           : false
    * rack.session                                            : #<ActionDispatch::Request::Session:0x00007fd03c333f88>
    * rack.session.options                                    : #<ActionDispatch::Request::Session::Options:0x00007fd03c333f38>
    * rack.tempfiles                                          : []
    * rack.url_scheme                                         : https
    * rack.version                                            : [1, 6]
    * warden                                                  : Warden::Proxy:70266169958160 @config={:default_scope=>:user, :scope_defaults=>{}, :default_strategies=>{:user=>[:rememberable, :database_authenticatable]}, :intercept_401=>false, :failure_app=>#<Devise::Delegator:0x0000558d2d22ea10>}

-------------------------------
Data:
-------------------------------

  * data: {:current_user=>
    #<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "[[email protected]](mailto:[email protected])", moderator: true, can_view_restricted_pages: true, deliver_analytics_report: true, can_manage_api: true, can_manage_subdomain_settings: true, session_timeoutable_in: "1-week", can_access_admin: true, deliver_error_notifications: true, can_manage_analytics: true, can_manage_files: true>,
   :current_visit=>
    #<Ahoy::Visit:0x00007fd03c4fb190
     id: 24986,
     visit_token: "5baffb58-186f-4fb7-8e0a-b855245a9432",
     visitor_token: "1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e",
     user_id: 1,
     ip: "142.181.192.212",
     user_agent:
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
     referrer: nil,
     referring_domain: nil,
     landing_page: "https://www.restarone.solutions/",
     browser: "Safari",
     os: "Mac",
     device_type: "Desktop",
     country: "CA",
     region: "Ontario",
     city: "Toronto",
     latitude: 43.6655,
     longitude: -79.4378,
     utm_source: nil,
     utm_medium: nil,
     utm_term: nil,
     utm_content: nil,
     utm_campaign: nil,
     app_version: nil,
     os_version: nil,
     platform: nil,
     started_at: Thu, 23 Jun 2022 12:07:23.782883000 UTC +00:00>}

Important error data (user + visit)

We see the user who encountered the error

* data: {:current_user=>
    #<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "[[email protected]](mailto:[email protected])", moderator: true, can_view_restricted_pages: true, deliver_analytics_report: true, can_manage_api: true, can_manage_subdomain_settings: true, session_timeoutable_in: "1-week", can_access_admin: true, deliver_error_notifications: true, can_manage_analytics: true, can_manage_files: true>,

we also see their visit

    #<Ahoy::Visit:0x00007fd03c4fb190
     id: 24986,
     visit_token: "5baffb58-186f-4fb7-8e0a-b855245a9432",
     visitor_token: "1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e",
     user_id: 1,
     ip: "142.181.192.212",
     user_agent:
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
     referrer: nil,
     referring_domain: nil,
     landing_page: "https://www.restarone.solutions/",
     browser: "Safari",
     os: "Mac",
     device_type: "Desktop",
     country: "CA",
     region: "Ontario",
     city: "Toronto",
     latitude: 43.6655,
     longitude: -79.4378,
     utm_source: nil,
     utm_medium: nil,
     utm_term: nil,
     utm_content: nil,
     utm_campaign: nil,
     app_version: nil,
     os_version: nil,
     platform: nil,
     started_at: Thu, 23 Jun 2022 12:07:23.782883000 UTC +00:00>}

Sensible tuning for your mental health

Not all errors need to be reported, so we suppress errors that are a fact of life for a web server on the internet. See the errors we suppress here: https://github.com/restarone/violet_rails/blob/master/config/environments/production.rb#L136-L141

Change management

Change management is the process we use to create, deploy and manage changes on a Violet Rails system.

CMS

In the CMS, each page can be part of 3 releases. The current release (named after the public path), the staged release (next version, named after the public path + name + -staging) and a backup of the current release (named after the public path-release-date-etc`)

Example

Here we see for the page / (that is named root) the current release is named root and is mapped to /. A new page root-staging has been created that maps to /root-staging. Right before cutting over, we will copy the contents of the root page (in raw HTML format, not with WSIWYG encoding) and create a new page named 'root-pre-etc' that will map to /root-pre-etc.

Incident response

If you find a Violet Rails system unresponsive. Please follow these steps to debug and remediate.

Make sure Violet Images are being backed up

For customers running on AWS, we use Lifecycle manager to grab snapshots of EC2 instances to create AMI's. These AMI's can be used to launch fresh instances of an app

Customer First!

Do not try to debug on a dead/dying system. Respond to the issue by launch a fresh instance to restore service, instead of trying to debug and fix in real time.

Check if NGINX is working

Check the NGINX access log, make sure the requests are hitting the reverse proxy

tail -f /var/log/nginx/access.log

If the access log looks good, check the error log

tail -f /var/log/nginx/error.log

Check if Puma is working

sudo systemctl status puma.service

Check if Sidekiq is working

sudo systemctl status sidekiq.service

Restart services if they are in a bad state

sudo systemctl restart puma.service
sudo systemctl restart sidekiq.service
sudo systemctl restart nginx