postjob 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/postjob.rb +22 -13
- data/lib/postjob/cli/events.rb +60 -0
- data/lib/postjob/cli/heartbeat.rb +55 -0
- data/lib/postjob/cli/hosts.rb +67 -0
- data/lib/postjob/cli/ps.rb +1 -13
- data/lib/postjob/cli/sessions.rb +83 -0
- data/lib/postjob/job.rb +4 -15
- data/lib/postjob/migrations/003_postjobs.sql +10 -8
- data/lib/postjob/migrations/003b_processing_columns.sql +8 -8
- data/lib/postjob/migrations/005_helpers.sql +3 -1
- data/lib/postjob/migrations/006_enqueue.sql +3 -0
- data/lib/postjob/migrations/006a_processing.sql +6 -26
- data/lib/postjob/migrations/007_job_results.sql +32 -13
- data/lib/postjob/migrations/008_checkout_runnable.sql +15 -21
- data/lib/postjob/migrations/008a_childjobs.sql +13 -0
- data/lib/postjob/migrations/010_settings.sql +18 -3
- data/lib/postjob/migrations/011_null_uuid.sql +7 -0
- data/lib/postjob/migrations/012_hosts.sql +42 -0
- data/lib/postjob/migrations/013_worker_sessions.sql +44 -0
- data/lib/postjob/migrations/014_postjob_session_id.sql +17 -0
- data/lib/postjob/migrations/015_events.sql +76 -0
- data/lib/postjob/migrations/016_sessions_functions.sql +16 -0
- data/lib/postjob/migrations/017_zombie_check.sql +58 -0
- data/lib/postjob/migrations/018_heartbeat.sql +28 -0
- data/lib/postjob/migrations/019_heartbeat_indices.sql +5 -0
- data/lib/postjob/queue.rb +41 -27
- data/lib/postjob/queue/notifications.rb +5 -4
- data/lib/postjob/queue/search.rb +2 -0
- data/lib/postjob/queue/settings.rb +11 -1
- data/lib/postjob/record.rb +17 -0
- data/lib/postjob/runner.rb +9 -2
- data/lib/postjob/worker_session.rb +76 -0
- data/lib/postjob/workflow.rb +0 -4
- data/lib/tools/atomic_store.rb +17 -0
- data/lib/tools/heartbeat.rb +151 -0
- data/lib/tools/history.rb +25 -0
- data/spec/postjob/events/heartbeat_event_spec.rb +85 -0
- data/spec/postjob/events/job_event_spec.rb +80 -0
- data/spec/postjob/job_control/max_attempts_spec.rb +0 -2
- data/spec/postjob/queue/search_spec.rb +0 -14
- data/spec/postjob/worker_session_spec.rb +41 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/test_helper.rb +11 -1
- metadata +43 -3
- data/spec/postjob/job_control/workflow_status_spec.rb +0 -52
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 00a785d7f9bd7a640601fb385902e157a76bf02a
         | 
| 4 | 
            +
              data.tar.gz: 7368f6f79e2f0392979656ff05bd12acc20751e9
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a09cd7a9014e1dddf95f493b064ae8e818ff65b98fd0db3ba15bf8de50e499b5d8c45ec919db52f994692c14b0550aa184dd74d9d4b76ba033c4109de59562f5
         | 
| 7 | 
            +
              data.tar.gz: 748047574250f33ea51cad3a823ecb2c7d66afa7263c4388d66157a0e58948433676935816794dde089b0c8f1cbcf10c24de6310723d12f61e578a6a08c4986e
         | 
    
        data/lib/postjob.rb
    CHANGED
    
    | @@ -12,6 +12,7 @@ end | |
| 12 12 | 
             
            require_relative "postjob/workflow"
         | 
| 13 13 | 
             
            require_relative "postjob/registry"
         | 
| 14 14 | 
             
            require_relative "postjob/job"
         | 
| 15 | 
            +
            require_relative "postjob/worker_session"
         | 
| 15 16 | 
             
            require_relative "postjob/error"
         | 
| 16 17 | 
             
            require_relative "postjob/queue"
         | 
| 17 18 | 
             
            require_relative "postjob/runner"
         | 
| @@ -52,12 +53,12 @@ module Postjob | |
| 52 53 | 
             
                end
         | 
| 53 54 |  | 
| 54 55 | 
             
                tags = stringify_hash(tags) if tags
         | 
| 55 | 
            -
                job = Queue.enqueue_job workflow, *args, queue: queue,
         | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 56 | 
            +
                job = Queue.enqueue_job current_worker_session.id, workflow, *args, queue: queue,
         | 
| 57 | 
            +
                                                                                    parent_id: parent_id,
         | 
| 58 | 
            +
                                                                                    max_attempts: max_attempts,
         | 
| 59 | 
            +
                                                                                    timeout: timeout,
         | 
| 60 | 
            +
                                                                                    tags: tags,
         | 
| 61 | 
            +
                                                                                    version: version
         | 
| 61 62 | 
             
                logger.info "Generated process #{job}"
         | 
| 62 63 | 
             
                job.id
         | 
| 63 64 | 
             
              end
         | 
| @@ -120,7 +121,7 @@ module Postjob | |
| 120 121 | 
             
                  break if shutdown == :shutdown
         | 
| 121 122 |  | 
| 122 123 | 
             
                  next if processed_job_id
         | 
| 123 | 
            -
                  Queue::Notifications.wait_for_new_job
         | 
| 124 | 
            +
                  Queue::Notifications.wait_for_new_job(current_worker_session.id)
         | 
| 124 125 | 
             
                end
         | 
| 125 126 |  | 
| 126 127 | 
             
                processed_jobs_count
         | 
| @@ -139,10 +140,16 @@ module Postjob | |
| 139 140 | 
             
              #
         | 
| 140 141 | 
             
              # or nil, when no job could be checked out.
         | 
| 141 142 | 
             
              def step
         | 
| 142 | 
            -
                job = Queue.checkout( | 
| 143 | 
            +
                job = Postjob::Queue.checkout(current_worker_session.id)
         | 
| 143 144 | 
             
                [ job.id, process_job(job) ] if job
         | 
| 144 145 | 
             
              end
         | 
| 145 146 |  | 
| 147 | 
            +
              # This method connects to the queue. This means it registers as a new worker_session,
         | 
| 148 | 
            +
              # if there was no worker_session yet.
         | 
| 149 | 
            +
              def current_worker_session
         | 
| 150 | 
            +
                @worker_session ||= WorkerSession.start!(Registry.workflows_with_versions)
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 146 153 | 
             
              private
         | 
| 147 154 |  | 
| 148 155 | 
             
              # This method is called from tests. Otherwise it is supposed to be private.
         | 
| @@ -158,11 +165,13 @@ module Postjob | |
| 158 165 | 
             
                  raise "Integrity check failed: job's workflow version changed (from #{job.workflow_version} to #{version})"
         | 
| 159 166 | 
             
                end
         | 
| 160 167 |  | 
| 168 | 
            +
                worker_session_id = current_worker_session.id
         | 
| 169 | 
            +
             | 
| 161 170 | 
             
                case status
         | 
| 162 | 
            -
                when :failed    then Queue.set_job_error job, *value, status: :failed, version: version
         | 
| 163 | 
            -
                when :err       then Queue.set_job_error job, *value, status: :err, version: version
         | 
| 164 | 
            -
                when :pending   then Queue.set_job_pending job, version: version
         | 
| 165 | 
            -
                when :ok        then Queue.set_job_result job, value, version: version
         | 
| 171 | 
            +
                when :failed    then Queue.set_job_error worker_session_id, job, *value, status: :failed, version: version
         | 
| 172 | 
            +
                when :err       then Queue.set_job_error worker_session_id, job, *value, status: :err, version: version
         | 
| 173 | 
            +
                when :pending   then Queue.set_job_pending worker_session_id, job, version: version
         | 
| 174 | 
            +
                when :ok        then Queue.set_job_result worker_session_id, job, value, version: version
         | 
| 166 175 | 
             
                else            raise ArgumentError, "Invalid status #{status.inspect}"
         | 
| 167 176 | 
             
                end
         | 
| 168 177 |  | 
| @@ -175,7 +184,7 @@ module Postjob | |
| 175 184 | 
             
                job = Queue.find_job_by_token(token)
         | 
| 176 185 | 
             
                raise "No job with token #{token}" unless job
         | 
| 177 186 |  | 
| 178 | 
            -
                Queue.set_job_result job, result, version: nil
         | 
| 187 | 
            +
                Queue.set_job_result current_worker_session.id, job, result, version: nil
         | 
| 179 188 | 
             
              end
         | 
| 180 189 |  | 
| 181 190 | 
             
              def register_workflow(workflow, options = {})
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            # rubocop:disable Lint/HandleExceptions
         | 
| 2 | 
            +
            # rubocop:disable Metrics/MethodLength
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Postjob::CLI
         | 
| 5 | 
            +
              private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def events_query(limit:)
         | 
| 8 | 
            +
                limit = Integer(limit)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                sql = <<-SQL
         | 
| 11 | 
            +
                  SELECT
         | 
| 12 | 
            +
                    events.id,
         | 
| 13 | 
            +
                    events.name,
         | 
| 14 | 
            +
                    events.postjob_id AS job_id,
         | 
| 15 | 
            +
                    postjobs.workflow
         | 
| 16 | 
            +
                      || (CASE WHEN postjobs.workflow_version != '' THEN '@' ELSE '' END)
         | 
| 17 | 
            +
                      || postjobs.workflow_version
         | 
| 18 | 
            +
                      || (CASE WHEN postjobs.workflow_method != 'run' THEN '.' || postjobs.workflow_method ELSE '' END)
         | 
| 19 | 
            +
                      || postjobs.args AS job,
         | 
| 20 | 
            +
                      worker_session_id,
         | 
| 21 | 
            +
                    events.created_at
         | 
| 22 | 
            +
                  FROM postjob.events events
         | 
| 23 | 
            +
                  LEFT JOIN postjob.postjobs postjobs ON events.postjob_id=postjobs.id
         | 
| 24 | 
            +
                  WHERE events.name != 'heartbeat'
         | 
| 25 | 
            +
                SQL
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                scope = Simple::SQL::Scope.new(sql)
         | 
| 28 | 
            +
                scope
         | 
| 29 | 
            +
                  .order_by("events.id DESC")
         | 
| 30 | 
            +
                  .paginate(per: limit, page: 1)
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              public
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              # Show the latest job event
         | 
| 36 | 
            +
              #
         | 
| 37 | 
            +
              # Example:
         | 
| 38 | 
            +
              #
         | 
| 39 | 
            +
              #   postjob events
         | 
| 40 | 
            +
              def events(limit: "100")
         | 
| 41 | 
            +
                expect! limit => /\A\d+\z/
         | 
| 42 | 
            +
                limit = Integer(limit)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                connect_to_database!
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                query = events_query(limit: limit)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                print_results query: query
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              # Show up-to-date events information once per second
         | 
| 52 | 
            +
              def events_top(limit: "100")
         | 
| 53 | 
            +
                loop do
         | 
| 54 | 
            +
                  system "clear"
         | 
| 55 | 
            +
                  events(limit: limit)
         | 
| 56 | 
            +
                  sleep 1
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              rescue Interrupt
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            # rubocop:disable Lint/HandleExceptions
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Postjob::CLI
         | 
| 4 | 
            +
              private
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def heartbeat_query(limit:)
         | 
| 7 | 
            +
                limit = Integer(limit)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                sql = <<-SQL
         | 
| 10 | 
            +
                  SELECT
         | 
| 11 | 
            +
                    name,
         | 
| 12 | 
            +
                    postjob_id AS job_id,
         | 
| 13 | 
            +
                    host_id,
         | 
| 14 | 
            +
                    (attributes->>'uptime')::interval AS uptime,
         | 
| 15 | 
            +
                    to_char((attributes->>'cpu_load_1min')::float, '99D99') AS cpu_load,
         | 
| 16 | 
            +
                    attributes->>'net_in_1min' AS net_in,
         | 
| 17 | 
            +
                    attributes->>'net_out_1min' AS net_out,
         | 
| 18 | 
            +
                    attributes->>'net_errors_1min' AS net_errors,
         | 
| 19 | 
            +
                    now() at time zone 'utc' - events.created_at AS age
         | 
| 20 | 
            +
                  FROM postjob.events events
         | 
| 21 | 
            +
                  LEFT JOIN postjob.worker_sessions worker_sessions ON events.worker_session_id=worker_sessions.id
         | 
| 22 | 
            +
                  WHERE events.name = 'heartbeat'
         | 
| 23 | 
            +
                SQL
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                scope = Simple::SQL::Scope.new(sql)
         | 
| 26 | 
            +
                scope
         | 
| 27 | 
            +
                  .order_by("events.id DESC")
         | 
| 28 | 
            +
                  .paginate(per: limit, page: 1)
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              public
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              # Show the latest heartbeat events
         | 
| 34 | 
            +
              def heartbeat(limit: "100")
         | 
| 35 | 
            +
                expect! limit => /\A\d+\z/
         | 
| 36 | 
            +
                limit = Integer(limit)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                connect_to_database!
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                query = heartbeat_query(limit: limit)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                Postjob.logger.info "CPU load and friends are for the last minute"
         | 
| 43 | 
            +
                print_results query: query
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              # Show up-to-date heartbeat information once per second
         | 
| 47 | 
            +
              def heartbeat_top(limit: "100")
         | 
| 48 | 
            +
                loop do
         | 
| 49 | 
            +
                  system "clear"
         | 
| 50 | 
            +
                  heartbeat(limit: limit)
         | 
| 51 | 
            +
                  sleep 1
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              rescue Interrupt
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            # rubocop:disable Lint/HandleExceptions
         | 
| 2 | 
            +
            # rubocop:disable Metrics/MethodLength
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Postjob::CLI
         | 
| 5 | 
            +
              private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def hosts_query(limit:)
         | 
| 8 | 
            +
                limit = Integer(limit)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                sql = <<-SQL
         | 
| 11 | 
            +
                  SELECT
         | 
| 12 | 
            +
                    hosts.id,
         | 
| 13 | 
            +
                    hosts.attributes,
         | 
| 14 | 
            +
                    hosts.created_at,
         | 
| 15 | 
            +
                    heartbeat.attributes AS heartbeat,
         | 
| 16 | 
            +
                    heartbeat.created_at AS heartbeat_created_at
         | 
| 17 | 
            +
                  FROM postjob.hosts hosts
         | 
| 18 | 
            +
                  LEFT JOIN (
         | 
| 19 | 
            +
                    SELECT
         | 
| 20 | 
            +
                      worker_sessions.host_id,
         | 
| 21 | 
            +
                      MAX(events.id) AS event_id
         | 
| 22 | 
            +
                    FROM postjob.worker_sessions
         | 
| 23 | 
            +
                    LEFT JOIN postjob.events events ON events.worker_session_id=worker_sessions.id
         | 
| 24 | 
            +
                    WHERE events.name = 'heartbeat'
         | 
| 25 | 
            +
                    GROUP BY worker_sessions.host_id
         | 
| 26 | 
            +
                  ) q ON q.host_id=hosts.id
         | 
| 27 | 
            +
                  LEFT JOIN events heartbeat ON heartbeat.id=event_id
         | 
| 28 | 
            +
                SQL
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                scope = Simple::SQL::Scope.new(sql)
         | 
| 31 | 
            +
                scope
         | 
| 32 | 
            +
                  .order_by("hosts.created_at DESC NULLS LAST")
         | 
| 33 | 
            +
                  .paginate(per: limit, page: 1)
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              public
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              # Show hosts status
         | 
| 39 | 
            +
              #
         | 
| 40 | 
            +
              # This command lists all worker_sessions currently in the system.
         | 
| 41 | 
            +
              #
         | 
| 42 | 
            +
              # Example:
         | 
| 43 | 
            +
              #
         | 
| 44 | 
            +
              #   postjob hosts
         | 
| 45 | 
            +
              def hosts(limit: "100")
         | 
| 46 | 
            +
                expect! limit => /\A\d+\z/
         | 
| 47 | 
            +
                limit = Integer(limit)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                connect_to_database!
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                query = hosts_query(limit: limit)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                print_results query: query
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              # Show up-to-date hosts information once per second
         | 
| 57 | 
            +
              #
         | 
| 58 | 
            +
              #
         | 
| 59 | 
            +
              def hosts_top(limit: "100")
         | 
| 60 | 
            +
                loop do
         | 
| 61 | 
            +
                  system "clear"
         | 
| 62 | 
            +
                  hosts(limit: limit)
         | 
| 63 | 
            +
                  sleep 1
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              rescue Interrupt
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
    
        data/lib/postjob/cli/ps.rb
    CHANGED
    
    | @@ -28,18 +28,6 @@ module Postjob::CLI | |
| 28 28 | 
             
                    next_run_at - (now() at time zone 'utc') AS next_run_in,
         | 
| 29 29 | 
             
                    to_char(EXTRACT(EPOCH FROM (now() at time zone 'utc') - postjobs.created_at), '999999999.99') AS age,
         | 
| 30 30 |  | 
| 31 | 
            -
                    CASE
         | 
| 32 | 
            -
                    WHEN processing_started_at IS NOT NULL THEN
         | 
| 33 | 
            -
                      format(
         | 
| 34 | 
            -
                        '%s/%s',
         | 
| 35 | 
            -
                        to_char(EXTRACT(EPOCH FROM (now() at time zone 'utc') - processing_started_at), '999999999.99'),
         | 
| 36 | 
            -
                        processing_max_duration
         | 
| 37 | 
            -
                      )
         | 
| 38 | 
            -
                    WHEN status IN ('failed', 'err', 'ok') THEN
         | 
| 39 | 
            -
                      format('%s', to_char(EXTRACT(EPOCH FROM (updated_at - created_at)), '999999999.99'))
         | 
| 40 | 
            -
                    END AS processing,
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                    COALESCE(processing_client, '') || COALESCE('/' || processing_client_identifier, '') AS worker,
         | 
| 43 31 | 
             
                    tags
         | 
| 44 32 | 
             
                  FROM postjob.postjobs AS postjobs
         | 
| 45 33 | 
             
                SQL
         | 
| @@ -172,7 +160,7 @@ module Postjob::CLI | |
| 172 160 | 
             
                tp records
         | 
| 173 161 |  | 
| 174 162 | 
             
                if records.total_count > records.length
         | 
| 175 | 
            -
                  logger.warn "Output limited up to limit #{records.length}. Use the --limit command line option for a different limit."
         | 
| 163 | 
            +
                  logger.warn "Output limited up to limit #{records.length}. Use the --limit=<NN> command line option for a different limit."
         | 
| 176 164 | 
             
                end
         | 
| 177 165 |  | 
| 178 166 | 
             
                if records.empty? && on_empty
         | 
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            # rubocop:disable Lint/HandleExceptions
         | 
| 2 | 
            +
            # rubocop:disable Metrics/MethodLength
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Postjob::CLI
         | 
| 5 | 
            +
              private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def sessions_query(limit:)
         | 
| 8 | 
            +
                limit = Integer(limit)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                sql = <<-SQL
         | 
| 11 | 
            +
                  SELECT
         | 
| 12 | 
            +
                    worker_sessions.id,
         | 
| 13 | 
            +
                    worker_sessions.host_id,
         | 
| 14 | 
            +
                    worker_sessions.client_socket,
         | 
| 15 | 
            +
                    worker_sessions.workflows,
         | 
| 16 | 
            +
                    worker_sessions.created_at,
         | 
| 17 | 
            +
                    job_event.name AS event_name,
         | 
| 18 | 
            +
                    job_event.created_at AS event_created_at,
         | 
| 19 | 
            +
                    heartbeat.attributes AS heartbeat,
         | 
| 20 | 
            +
                    heartbeat.created_at AS heartbeat_created_at
         | 
| 21 | 
            +
                  FROM postjob.worker_sessions AS worker_sessions
         | 
| 22 | 
            +
                  LEFT JOIN (
         | 
| 23 | 
            +
                    SELECT
         | 
| 24 | 
            +
                      worker_sessions.id,
         | 
| 25 | 
            +
                      MAX(events.id) AS event_id
         | 
| 26 | 
            +
                    FROM postjob.worker_sessions
         | 
| 27 | 
            +
                    LEFT JOIN postjob.events events ON events.worker_session_id=worker_sessions.id
         | 
| 28 | 
            +
                    WHERE events.name != 'heartbeat'
         | 
| 29 | 
            +
                    GROUP BY worker_sessions.id
         | 
| 30 | 
            +
                  ) last_job_event ON last_job_event.id=worker_sessions.id
         | 
| 31 | 
            +
                  LEFT JOIN postjob.events job_event ON job_event.id=last_job_event.event_id
         | 
| 32 | 
            +
                  LEFT JOIN (
         | 
| 33 | 
            +
                    SELECT
         | 
| 34 | 
            +
                      worker_sessions.id,
         | 
| 35 | 
            +
                      MAX(events.id) AS event_id
         | 
| 36 | 
            +
                    FROM postjob.worker_sessions
         | 
| 37 | 
            +
                    LEFT JOIN postjob.events events ON events.worker_session_id=worker_sessions.id
         | 
| 38 | 
            +
                    WHERE events.name = 'heartbeat'
         | 
| 39 | 
            +
                    GROUP BY worker_sessions.id
         | 
| 40 | 
            +
                  ) last_heartbeat ON last_heartbeat.id=worker_sessions.id
         | 
| 41 | 
            +
                  LEFT JOIN postjob.events heartbeat ON heartbeat.id=last_heartbeat.event_id
         | 
| 42 | 
            +
                SQL
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                scope = Simple::SQL::Scope.new(sql)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                scope
         | 
| 47 | 
            +
                  .paginate(per: limit, page: 1)
         | 
| 48 | 
            +
                  .order_by("heartbeat_created_at DESC NULLS LAST")
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              public
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              # Show sessions status
         | 
| 54 | 
            +
              #
         | 
| 55 | 
            +
              # This command lists all worker sessions currently in the system.
         | 
| 56 | 
            +
              #
         | 
| 57 | 
            +
              # Example:
         | 
| 58 | 
            +
              #
         | 
| 59 | 
            +
              #   postjob sessions
         | 
| 60 | 
            +
              def sessions(limit: "100")
         | 
| 61 | 
            +
                expect! limit => /\A\d+\z/
         | 
| 62 | 
            +
                limit = Integer(limit)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                connect_to_database!
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                # check for timed out and zombie processes
         | 
| 67 | 
            +
                # ::Postjob::Queue.checkout(nil)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                query = sessions_query(limit: limit)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                print_results query: query
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              # Show up-to-date session information once per second
         | 
| 75 | 
            +
              def sessions_top(limit: "100")
         | 
| 76 | 
            +
                loop do
         | 
| 77 | 
            +
                  system "clear"
         | 
| 78 | 
            +
                  sessions(limit: limit)
         | 
| 79 | 
            +
                  sleep 1
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              rescue Interrupt
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
            end
         | 
    
        data/lib/postjob/job.rb
    CHANGED
    
    | @@ -1,25 +1,14 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            # rubocop:disable Security/Eval
         | 
| 1 | 
            +
            require_relative "./record"
         | 
| 3 2 |  | 
| 4 3 | 
             
            #
         | 
| 5 | 
            -
            # A job | 
| 4 | 
            +
            # A job
         | 
| 6 5 | 
             
            #
         | 
| 7 | 
            -
            class Postjob::Job <  | 
| 8 | 
            -
              def initialize(hsh)
         | 
| 9 | 
            -
                replace hsh.dup
         | 
| 10 | 
            -
              end
         | 
| 11 | 
            -
             | 
| 6 | 
            +
            class Postjob::Job < Postjob::Record
         | 
| 12 7 | 
             
              def self.find(job_id)
         | 
| 13 8 | 
             
                scope = Postjob::Queue.search(id: job_id)
         | 
| 14 9 | 
             
                Simple::SQL.ask(scope, into: Postjob::Job)
         | 
| 15 10 | 
             
              end
         | 
| 16 11 |  | 
| 17 | 
            -
              def self.attribute(sym)
         | 
| 18 | 
            -
                eval <<~RUBY
         | 
| 19 | 
            -
                  define_method(:#{sym}) { self[:#{sym}] }
         | 
| 20 | 
            -
                RUBY
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
             | 
| 23 12 | 
             
              attribute :id
         | 
| 24 13 | 
             
              attribute :parent_id
         | 
| 25 14 | 
             
              attribute :full_id
         | 
| @@ -40,9 +29,9 @@ class Postjob::Job < Hash | |
| 40 29 | 
             
              attribute :error_message
         | 
| 41 30 | 
             
              attribute :error_backtrace
         | 
| 42 31 | 
             
              attribute :recipients
         | 
| 43 | 
            -
              attribute :workflow_status
         | 
| 44 32 | 
             
              attribute :timed_out
         | 
| 45 33 | 
             
              attribute :tags
         | 
| 34 | 
            +
              attribute :last_worker_session_id
         | 
| 46 35 |  | 
| 47 36 | 
             
              STATUSES = %w(ok ready processing sleep err failed timeout)
         | 
| 48 37 |  | 
| @@ -38,6 +38,8 @@ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.postjobs ( | |
| 38 38 | 
             
              -- Number of failed attempts so far.
         | 
| 39 39 | 
             
              failed_attempts INTEGER NOT NULL DEFAULT 0,
         | 
| 40 40 |  | 
| 41 | 
            +
              -- last_worker_session_id UUID NOT NULL REFERENCES {SCHEMA_NAME}.worker_sessions ON DELETE CASCADE,
         | 
| 42 | 
            +
             | 
| 41 43 | 
             
              -- process result ---------------------------------------------------------------------------------------
         | 
| 42 44 |  | 
| 43 45 | 
             
              results JSONB,            -- The process result, if any. Only valid when status == 'ok'
         | 
| @@ -46,8 +48,8 @@ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.postjobs ( | |
| 46 48 | 
             
              error_backtrace JSONB,    -- additional error information, for debugging purposes
         | 
| 47 49 |  | 
| 48 50 | 
             
              -- custom fields ----------------------------------------------------------------------------------------
         | 
| 49 | 
            -
              workflow_status VARCHAR,
         | 
| 50 | 
            -
              tags JSONB | 
| 51 | 
            +
              -- workflow_status VARCHAR,
         | 
| 52 | 
            +
              tags JSONB
         | 
| 51 53 |  | 
| 52 54 | 
             
              -- processing_client information ------------------------------------------------------------------------
         | 
| 53 55 | 
             
              -- This information is passed along from workers during processing. They are only valid
         | 
| @@ -55,12 +57,12 @@ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.postjobs ( | |
| 55 57 | 
             
              --
         | 
| 56 58 | 
             
              -- Initially these columns didn't exist, and have been created via another migration
         | 
| 57 59 | 
             
              -- (003b_processing_columns.sql). They are listed here for documentation purposes.
         | 
| 58 | 
            -
              processing_client varchar, | 
| 59 | 
            -
              processing_client_identifier varchar, | 
| 60 | 
            -
              processing_started_at timestamp | 
| 61 | 
            -
              processing_max_duration float | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 60 | 
            +
              -- processing_client varchar,               -- host:port of client (taken from pg_stat_activity)
         | 
| 61 | 
            +
              -- processing_client_identifier varchar,    -- free text info, set via set_client_identifier()
         | 
| 62 | 
            +
              -- processing_started_at timestamp          -- when did processing start?
         | 
| 63 | 
            +
              -- processing_max_duration float            -- maximum expected duration of processing. Afterwards the
         | 
| 64 | 
            +
                                                          -- processing is considered failed for unknown reasons, and
         | 
| 65 | 
            +
                                                          -- potentially restarted.
         | 
| 64 66 | 
             
            );
         | 
| 65 67 |  | 
| 66 68 | 
             
            -- [TODO] check indices
         |