zammad/zammad

View on GitHub
lib/zammad/application/initializer/db_preflight_check/mysql2.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

# NOTE: Why use Mysql2::Client over ActiveRecord::Base.connection?
#
# As of Rails 5.2, db:create now runs initializers prior to creating the DB.
# That means if an initializer tries to establish an ActiveRecord::Base.connection,
# it will raise an ActiveRecord::NoDatabaseError
# (see https://github.com/rails/rails/issues/32870 for more details).
#
# The workaround is to use the bare RDBMS library
# and connect without specifying a database.

module Zammad
  class Application
    module Initializer
      module DbPreflightCheck
        module Mysql2
          extend Base

          def self.perform
            check_version_compatibility
            check_max_allowed_packet
          end

          # Configuration check --------------------------------------------------
          # Before MySQL 8.0, the default value of max_allowed_packet was 1MB - 4MB,
          # which is impractically small and can lead to email processing failures.
          #
          # See https://github.com/zammad/zammad/issues/1759
          #     https://github.com/zammad/zammad/issues/1970
          #     https://github.com/zammad/zammad/issues/2034
          def self.check_max_allowed_packet
            if max_allowed_packet_mb <= 4
              err(<<~MSG)
                Database config value 'max_allowed_packet' too small (#{max_allowed_packet_mb}MB)
                Please increase this value in your #{vendor} configuration (64MB+ recommended).
              MSG
            elsif max_allowed_packet_mb < Setting.get('postmaster_max_size').to_i
              warn(<<~MSG)
                Database config value 'max_allowed_packet' less than Zammad setting 'Maximum Email Size'
                Zammad will fail to process emails (both incoming and outgoing)
                larger than the value of 'max_allowed_packet' (#{max_allowed_packet_mb}MB).
                Please increase this value in your #{vendor} configuration accordingly.
              MSG
            end
          rescue ActiveRecord::StatementInvalid
            # Setting.get will fail if 'settings' table does not exist
          end

          def self.connection
            @connection ||= ::Mysql2::Client.new(db_config)
          end

          def self.db_config
            @db_config ||= ActiveRecord::Base.connection_db_config.configuration_hash
                                             .symbolize_keys
                                             .except(:database)
          end

          #  formats: "5.7.3" (MySQL)
          #           "10.1.17-MariaDB" (MariaDB)
          def self.current_version
            @current_version ||= mysql_variable('version').split('-').first
          end

          def self.min_version
            case vendor
            when 'MySQL'
              '5.7'
            when 'MariaDB'
              '10.3'
            end
          end

          def self.vendor
            @vendor ||= mysql_variable('version').split('-').second || 'MySQL'
          end

          def self.max_allowed_packet_mb
            @max_allowed_packet_mb ||= mysql_variable('max_allowed_packet') / 1024 / 1024
          end

          def self.mysql_variable(name)
            connection.query("SELECT @@#{name};").first["@@#{name}"]
          end
        end
      end
    end
  end
end