Skip to content

chore: solid_queue v1.5.0 → v1.5.1 — update Mongoid models for upstream changes #8

Description

@github-actions

solid_queue v1.5.1 — Upstream Model Changes

Comparing rails/solid_queue models between v1.5.0v1.5.1.
Each section shows what changed upstream alongside our current Mongoid model for context.
Review each diff and decide if the corresponding Mongoid model needs updating.

Summary

File Upstream Change
record.rb 🔄 Modified
record/distinct_values.rb 🔄 Modified

Review Checklist

  • lib/solid_queue_mongoid/models/record.rb — review upstream changes
  • lib/solid_queue_mongoid/models/record/distinct_values.rb — review upstream changes

Detailed Diffs


record.rb — 🔄 Modified upstream

📥 What changed in upstream (v1.5.0 → v1.5.1)
--- solid_queue@v1.5.0/record.rb
+++ solid_queue@v1.5.1/record.rb
@@ -24,6 +24,16 @@
         end
       end
 
+      def warn_about_pending_migrations
+        SolidQueue.deprecator.warn(<<~DEPRECATION)
+          Solid Queue has pending database migrations. To get the new migration files, run:
+            rails solid_queue:update
+          And then:
+            rails db:migrate
+          These migrations will be required after version #{SolidQueue.next_major_version}.0
+        DEPRECATION
+      end
+
       # Pass index hints to the query optimizer using SQL comment hints.
       # Uses MySQL 8 optimizer hint query comments, which SQLite and
       # PostgreSQL ignore.
📄 Our current Mongoid model — `lib/solid_queue_mongoid/models/record.rb`
# frozen_string_literal: true

module SolidQueue
  class Record
    include Mongoid::Document
    include Mongoid::Timestamps

    # Subclasses declare their MySQL index name → MongoDB field spec mappings here.
    INDEX_HINTS = {}.freeze

    # Override Mongoid's index_specifications to use per-class instance variables
    # instead of the shared cattr_accessor class variable.
    # This prevents index cross-contamination between models.
    class << self
      def index_specifications
        @index_specifications ||= []
      end

      def index_specifications=(val)
        @_sq_index_specs = val
      end

      # Override Mongoid's index() to use our per-class storage.
      def index(spec, options = nil)
        specification = Mongoid::Indexable::Specification.new(self, spec, options)
        return if index_specifications.include?(specification)

        index_specifications.push(specification)
      end
    end

    # Dynamic collection naming with prefix
    def self.inherited(subclass)
      super

      collection_name = subclass.name.demodulize.tableize
      prefixed_name = "#{SolidQueue.collection_prefix}#{collection_name}"

      subclass.store_in collection: prefixed_name, client: -> { SolidQueue.client.to_s }

      # Each subclass gets its own empty index_specifications array.
      # Indexes must be explicitly defined in each subclass (not inherited from parent)
      # because Mongoid::Indexable::Specification stores a reference to the klass,
      # and copying parent specs would create indexes on the parent's collection.
      subclass.instance_variable_set(:@_sq_index_specs, [])
    end

    class << self
      # MongoDB has no row-level locking; this is a no-op stub so SolidQueue
      # code that chains .non_blocking_lock still works.
      def non_blocking_lock
        all
      end

      # Translate solid_queue MySQL index names to MongoDB hint specs and apply
      # via Mongoid's .hint(). Unknown names are ignored (no hint applied).
      # Subclasses may override INDEX_HINTS to register their own mappings.
      def use_index(*indexes)
        specs = indexes.filter_map do |name|
          name.is_a?(Hash) ? name : self::INDEX_HINTS[name.to_sym]
        end
        specs.any? ? hint(specs.first) : all
      end

      # MongoDB supports unique indexes which serve the same purpose.
      def supports_insert_conflict_target?
        true
      end

      # Mongoid 9 supports multi-document transactions via replica sets.
      # We wrap in a MongoDB session transaction when available; fall back to a
      # plain yield for non-replica-set environments (e.g. tests with a standalone).
      def transaction(_requires_new: false, &block)
        Mongoid::QueryCache.clear_cache
        Mongoid.default_client.with_session do |session|
          session.start_transaction
          result = yield
          session.commit_transaction
          result
        end
      rescue Mongo::Error::InvalidSession, Mongo::Error::OperationFailure => e
        # Not in a replica set or session not supported — execute without transaction
        raise if e.message.to_s.include?("Transaction numbers are only allowed")

        yield
      rescue StandardError
        yield
      end

      # Mongoid equivalent of ActiveRecord's create_or_find_by!.
      # Tries to create; on duplicate-key error or uniqueness validation error
      # finds the existing record. Falls back to finding by job_id alone when
      # the full attrs lookup would miss the existing record (e.g. queue_name
      # is not in attrs but was set via a before_create callback).
      def create_or_find_by!(attrs, &block)
        record = new(attrs)
        block.call(record) if block_given?
        record.save!
        record
      rescue Mongoid::Errors::Validations => e
        # If the only errors are uniqueness-related, fall back to find the existing record
        raise unless uniqueness_only_error?(e.document)

        find_by_unique_key(attrs) || where(attrs).first || record
      rescue Mongo::Error::OperationFailure => e
        raise unless duplicate_key_error?(e)

        find_by_unique_key(attrs) || where(attrs).first || raise(e)
      end

      # find_by that raises Mongoid::Errors::DocumentNotFound when missing.
      def find_by!(attrs)
        find_by(attrs) || raise(Mongoid::Errors::DocumentNotFound.new(self, attrs))
      end

      private

      def duplicate_key_error?(err)
        msg = err.respond_to?(:message) ? err.message.to_s : err.to_s
        msg.include?("E11000") || msg.include?("duplicate key")
      end

      # Try to find an existing record using just the unique key field(s).
      # Used as fallback when find_by(full_attrs) misses because some fields
      # (e.g. queue_name) are only set by callbacks, not passed in attrs.
      # Uses where().first to avoid DocumentNotFound exceptions.
      def find_by_unique_key(attrs)
        return where(job_id: attrs[:job_id]).first if attrs[:job_id]
        return where(key: attrs[:key]).first if attrs[:key]

        nil
      end

      def uniqueness_only_error?(document)
        return false unless document.respond_to?(:errors)

        document.errors.all? do |error|
          error.type == :taken || error.message.to_s.include?("already been taken") ||
            (error.attribute.to_s != "base" &&
              document.class.validators
                      .select { |v| v.is_a?(Mongoid::Validatable::UniquenessValidator) }
                      .any? { |v| v.attributes.include?(error.attribute.to_sym) })
        end
      end
    end
  end
end

record/distinct_values.rb — 🔄 Modified upstream

📥 What changed in upstream (v1.5.0 → v1.5.1)
--- solid_queue@v1.5.0/record/distinct_values.rb
+++ solid_queue@v1.5.1/record/distinct_values.rb
@@ -19,23 +19,25 @@
 
         private
           def loose_index_scan_emulation_needed?
-            connection.adapter_name == "PostgreSQL"
+            connection_pool.with_connection { |connection| connection.adapter_name == "PostgreSQL" }
           end
 
           # Emulates a loose index scan, honoring the current scope (e.g. LIKE prefixes)
           # by building the anchor and the recursive step as scoped relations, whose
           # #to_sql inlines any bind parameters so they can be embedded in the raw CTE.
           def loose_distinct_via_recursive_cte(column)
-            col = connection.quote_column_name(column)
+            connection_pool.with_connection do |connection|
+              col = connection.quote_column_name(column)
 
-            connection.select_values(<<~SQL.squish)
-              WITH RECURSIVE t AS (
-                (#{next_distinct_value(col, "#{col} IS NOT NULL")})
-                UNION ALL
-                SELECT (#{next_distinct_value(col, "#{col} > t.#{col}")}) FROM t WHERE t.#{col} IS NOT NULL
-              )
-              SELECT #{col} FROM t WHERE #{col} IS NOT NULL
-            SQL
+              connection.select_values(<<~SQL.squish)
+                WITH RECURSIVE t AS (
+                  (#{next_distinct_value(col, "#{col} IS NOT NULL")})
+                  UNION ALL
+                  SELECT (#{next_distinct_value(col, "#{col} > t.#{col}")}) FROM t WHERE t.#{col} IS NOT NULL
+                )
+                SELECT #{col} FROM t WHERE #{col} IS NOT NULL
+              SQL
+            end
           end
 
           # Smallest value of `col` within the current scope that matches `condition`.
📄 Our current Mongoid model — `lib/solid_queue_mongoid/models/record/distinct_values.rb`

No local equivalent exists for this file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    upstream-syncsync from solid queue upstream

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions