# frozen_string_literal: true # Genera resúmenes de información para poder mostrar estadísticas y se # corre regularmente a sí misma. class StatCollectionJob < ApplicationJob class CrontabException < StandardError; end # Descartar y notificar si pasó algo más. # # XXX: En realidad deberíamos seguir reintentando? discard_on(Exception) do |_, error| ExceptionNotifier.notify_exception error end # Correr indefinidamente una vez por hora. # # XXX: El orden importa, si el descarte viene después, nunca se va a # reintentar. retry_on(StatCollectionJob::CrontabException, wait: 1.hour, attempts: Float::INFINITY, jitter: 0) COLUMNS = %i[uri].freeze def perform(once: false) Site.find_each do |site| hostnames = [site.hostname, site.alternative_hostnames].flatten # Usamos el primero porque luego podemos hacer un rollup recursivo options = { interval: Stat::INTERVALS.first } # Visitas por hostname hostnames.each do |hostname| AccessLog.where(host: hostname).completed_requests.non_robots.pages.group(:host).rollup('host', **options) combined_columns(hostname, **options) end stats_by_site(site, **options) end # Registrar que se hicieron todas las recolecciones Stat.create! raise CrontabException unless once end private # Combinación de columnas def combined_columns(hostname, **options) where = { host: hostname } COLUMNS.each do |column| AccessLog.where(host: hostname).pluck(Arel.sql("distinct #{column}")).each do |value| where[column] = value AccessLog.where(**where).completed_requests.non_robots.group(:host, column).rollup("host|#{column}", **options) end end end # Uso de recursos por cada sitio. # # XXX: En realidad se agrupan por el deploy_id, que siempre será el # del DeployLocal. def stats_by_site(site, **options) site.build_stats.jekyll.group(:deploy_id).rollup('builds', **options) site.build_stats.jekyll.group(:deploy_id).rollup('space_used', **options) do |rollup| rollup.average(:bytes) end site.build_stats.jekyll.group(:deploy_id).rollup('build_time', **options) do |rollup| rollup.average(:seconds) end end end