Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

finished_at_start/end job filters #111

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

Xeitor
Copy link
Contributor

@Xeitor Xeitor commented Apr 5, 2024

Changelog

  • added finished_at range filter support for jobs

Issue: #30

PD: I'll split the different filters in different PRs for easier review

@Xeitor Xeitor marked this pull request as draft April 5, 2024 21:57
@Xeitor Xeitor marked this pull request as ready for review April 7, 2024 14:38
@Xeitor
Copy link
Contributor Author

Xeitor commented Apr 9, 2024

Hi @rosa! Don't mean to bother you but in case you missed this this PR is ready for review :)

@rosa
Copy link
Member

rosa commented Apr 10, 2024

@Xeitor thanks for letting me know, I'll try to review soon!

@Xeitor
Copy link
Contributor Author

Xeitor commented May 15, 2024

Hi @rosa, hope you are doing great

Sorry to bother you, wanted to know if you were able to take a look at the PR :)

@Xeitor Xeitor requested a review from rosa August 18, 2024 13:34
@rosa
Copy link
Member

rosa commented Sep 13, 2024

Hey @Xeitor, so sorry about this huge delay! I haven't had time to focus on Mission Control lately, but it's definitely in the plans for October, I need to spend a week or so just working on all pending PRs and missing features 😳

@Xeitor
Copy link
Contributor Author

Xeitor commented Sep 25, 2024

Hey @Xeitor, so sorry about this huge delay! I haven't had time to focus on Mission Control lately, but it's definitely in the plans for October, I need to spend a week or so just working on all pending PRs and missing features 😳

for sure no problem

@rosa
Copy link
Member

rosa commented Nov 4, 2024

Hey @Xeitor, I'm finally back with Mission Control. Are you still interested in working on this one? I'd understand if not, given how long it's been, so sorry about that! If you're no longer interested, I'm happy to take it from here.

@Xeitor
Copy link
Contributor Author

Xeitor commented Nov 8, 2024

hi @rosa, glad to have you back. For sure, no worries, I 'll take a look at the comments in the next days

Comment on lines 59 to 63
worker_id: worker_id,
recurring_task_id: recurring_task_id
recurring_task_id: recurring_task_id,
}.compact.collect { |key, value| [ key, value.to_s ] }.to_h

arguments.merge!(finished_at: finished_at) if finished_at.present? && !finished_at.to_s.match?(/^..$/)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Rails does the right thing when you provide a beginless and endless range in a condition value, so you don't need to worry about checking this here. This can be kept as:

arguments = { job_class_name: job_class_name,
      queue_name: queue_name,
      worker_id: worker_id,
      recurring_task_id: recurring_task_id,
      finished_at: finished_at,
    }.compact.collect { |key, value| [ key, value.to_s ] }.to_h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this, for example:

dummy(dev)> hash = { finished_at: nil..nil }.compact
=> {:finished_at=>nil..}
dummy(dev)> hash.collect { |key, value| [ key, value.to_s ] }.to_h
=> {:finished_at=>".."}

will override finished_at to an empty range

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's not an empty range, but an "infinite" range, a range without start and end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

besides, I did not want not stringity the range, check out this behaviour:

dummy(dev)> ActiveJob.jobs.finished.where(finished_at: (..Time.now).to_s).first
  SolidQueue::Job Load (0.2ms)  SELECT "solid_queue_jobs".* FROM "solid_queue_jobs" WHERE "solid_queue_jobs"."finished_at" IS NOT NULL AND "solid_queue_jobs"."finished_at" = ? ORDER BY "solid_queue_jobs"."finished_at" DESC LIMIT ? OFFSET ?  [["finished_at", "2024-11-17 19:53:22"], ["LIMIT", 1000], ["OFFSET", 0]]
=> nil
dummy(dev)> ActiveJob.jobs.finished.where(finished_at: ..Time.now).first
  SolidQueue::Job Load (0.5ms)  SELECT "solid_queue_jobs".* FROM "solid_queue_jobs" WHERE "solid_queue_jobs"."finished_at" IS NOT NULL AND "solid_queue_jobs"."finished_at" <= ? ORDER BY "solid_queue_jobs"."finished_at" DESC LIMIT ? OFFSET ?  [["finished_at", "2024-11-17 19:53:28.197854"], ["LIMIT", 1000], ["OFFSET", 0]]
=>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you convert the range into a string? That wouldn't work in the query.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}.compact.collect { |key, value| [ key, value.to_s ] }.to_h

this line will do the .to_s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and sorry yes you are right, we could just do: arguments.merge!(finished_at: finished_at) without the condition I wrote

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry! Yes, we don't want to do that to the range. My comment was more addressed at this part:

if finished_at.present? && !finished_at.to_s.match?(/^..$/)

I meant we don't need that and we could just initialize arguments[:finished_at] with the value.

In any case, I think we can get rid of the to_s call on the values 🤔 It was added here because we didn't have the compact before, but I don't think we need it. You can try to remove it and see if any tests fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great!! thanks :)

@@ -40,7 +40,7 @@ def supported_job_statuses
end

def supported_job_filters(*)
[ :queue_name, :job_class_name ]
[ :queue_name, :job_class_name, :finished_at_start, :finished_at_end ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want finished_at here 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no, this had to be split into supported_natively and supported_in_memory, but in any case, it should be just finished_at when added to supported_natively.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had something like this in mind:

diff --git a/lib/active_job/jobs_relation.rb b/lib/active_job/jobs_relation.rb
index e4e19ee..a92af46 100644
--- a/lib/active_job/jobs_relation.rb
+++ b/lib/active_job/jobs_relation.rb
@@ -23,7 +23,6 @@ class ActiveJob::JobsRelation
   include Enumerable
                                                                                                                                      
   STATUSES = %i[ pending failed in_progress blocked scheduled finished ]
-  FILTERS = %i[ queue_name job_class_name finished_at ]
                                                                                                                                      
   PROPERTIES = %i[ queue_name status offset_value limit_value job_class_name worker_id recurring_task_id finished_at ]
   attr_reader *PROPERTIES, :default_page_size
@@ -276,8 +275,7 @@ class ActiveJob::JobsRelation
     end
                                                                                                                                      
     def filters
-      # @filetrs ||= queue_adapter.supported_job_filters(self).select { |property| public_send(property).present? && !queue_adapter.natively_supported_job_filters?(self, property) }
-      @filters ||= FILTERS.select { |property| public_send(property).present? && !queue_adapter.supports_job_filter?(self, property) }
+      @filters ||= queue_adapter.supported_job_filters(self).select { |property| public_send(property).present? && !queue_adapter.natively_supported_job_filters(self).include?(property) }
     end
                                                                                                                                      
     def ensure_failed_status
diff --git a/lib/active_job/queue_adapters/resque_ext.rb b/lib/active_job/queue_adapters/resque_ext.rb
index 17b2fbf..d7d28af 100644
--- a/lib/active_job/queue_adapters/resque_ext.rb
+++ b/lib/active_job/queue_adapters/resque_ext.rb
@@ -48,6 +48,10 @@ module ActiveJob::QueueAdapters::ResqueExt
   end
                                                                                                                                      
   def supported_job_filters(jobs_relation)
+    [ :queue_name, :job_class_name ]
+  end
+
+  def natively_supported_job_filters(jobs_relation)
     if jobs_relation.pending? then [ :queue_name ]
     else []
     end
diff --git a/lib/active_job/queue_adapters/solid_queue_ext.rb b/lib/active_job/queue_adapters/solid_queue_ext.rb
index 2db9ea1..b069504 100644
--- a/lib/active_job/queue_adapters/solid_queue_ext.rb
+++ b/lib/active_job/queue_adapters/solid_queue_ext.rb
@@ -39,8 +39,14 @@ module ActiveJob::QueueAdapters::SolidQueueExt
     SolidQueueJobs::STATUS_MAP.keys
   end
                                                                                                                                      
-  def supported_job_filters(*)
-    [ :queue_name, :job_class_name, :finished_at_start, :finished_at_end ]
+  def supported_job_filters(jobs_relation)
+    filters = [ :queue_name, :job_class_name ]
+    filters << :finished_at if jobs_relation.finished?
+    filters
+  end
+
+  def natively_supported_job_filters(*)
+    [ :queue_name, :job_class_name, :finished_at ]
   end
                                                                                                                                      
   def jobs_count(jobs_relation)

Copy link
Contributor Author

@Xeitor Xeitor Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also raise query errors if you try to use unsupported filters for a given job relation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think natively_supported_job_filters should take a job_relation as well and do the check jobs_relation.finished?. Then you just use that in supported_job_filters 🤔

Copy link
Contributor Author

@Xeitor Xeitor Nov 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a little recap to see if we both are in the same page:
given that JobsRelations is the interface to the adapters and therefore knows all the generic filters that can be applied: FILTERS (with the extra parametrs in where method), I think the most reasonable way to select the filters that should be performed in memory is to first ask the adapter if the filter is supported but not natively.

translating this thoughts to code I was thinking something like this:

def filters
  @filters ||= FILTERS.select do |property|
    public_send(property).present? && should_perform_filter_in_memory?(filter)
  end
end

def should_perform_filter_in_memory?(filter)
  # example: finished_at is supported only if the status is finished
  queue_adapter.supports_job_filter?(self, property) &&
    !queue_adapter.natively_supported_job_filters?(self).include?(property)
end

Xeitor and others added 2 commits November 17, 2024 16:57
Co-authored-by: Rosa Gutierrez <rosa.ge@gmail.com>
@MacLove13 MacLove13 mentioned this pull request Nov 20, 2024
@@ -274,6 +276,7 @@ def satisfy_filter?(job)
end

def filters
# @filetrs ||= queue_adapter.supported_job_filters(self).select { |property| public_send(property).present? && !queue_adapter.natively_supported_job_filters?(self, property) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# @filetrs ||= queue_adapter.supported_job_filters(self).select { |property| public_send(property).present? && !queue_adapter.natively_supported_job_filters?(self, property) }
# @filetrs ||= queue_adapter.supported_job_filters(self).select { |property| public_send(property).present? && !queue_adapter.supports_job_filter_natively?(self, property) }

To be consistent with the other method, supports_job_filter?.

@@ -60,7 +60,7 @@ module ActiveJob::QueueAdapters::AdapterTesting::DiscardJobs
5.times { FailingJob.perform_later }
10.times { FailingReloadedJob.perform_later }
perform_enqueued_jobs
ActiveJob.jobs.failed.where(queue_name: :queue_1).discard_all
ActiveJob.jobs.failed.where(queue_name: 'queue_1').discard_all
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh that is because we got rid of the .to_s in the JobsRelation#where method and made the test fail

Copy link
Member

@rosa rosa Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh, I see! In that case let's bring that back for queue_name. We want to be able to use symbols there! Sorry, I didn't think of that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep makes sense, no problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants