# Update the cached attribute
self.data[key] = val
+
+ def _set_attribute_now(self, key):
+ """
+ Sets the given key to CURRENT_TIMESTAMP
+ """
+ res = self.db.get("UPDATE %s SET %s = CURRENT_TIMESTAMP \
+ WHERE id = %%s RETURNING %s" % (self.table, key, key), self.id)
+
+ # Update cached attribute
+ if res:
+ self.data[key] = res[key]
FROM
jobs
WHERE
- time_started IS NOT NULL
+ started_at IS NOT NULL
AND
- time_finished IS NULL
+ finished_at IS NULL
AND
builder_id = %s
ORDER BY
- time_started""",
+ started_at DESC""",
self.id,
)
def get_by_uuid(self, uuid):
return self._get_job("SELECT * FROM jobs WHERE uuid = %s", uuid)
- def get_active(self, limit=None):
- jobs = self._get_jobs("SELECT jobs.* FROM jobs \
- WHERE time_started IS NOT NULL AND time_finished IS NULL \
- ORDER BY time_started LIMIT %s", limit)
+ @property
+ def running(self):
+ jobs = self._get_jobs("""
+ SELECT
+ jobs.*
+ FROM
+ jobs
+ WHERE
+ started_at IS NOT NULL
+ AND
+ finished_at IS NULL
+ ORDER BY
+ finished_at
+ """)
- return jobs
+ return list(jobs)
def get_recently_ended(self, limit=None):
jobs = self._get_jobs("SELECT jobs.* FROM jobs \
superseeded_by = lazy_property(get_superseeded_by, set_superseeded_by)
- def start(self, builder):
+ @property
+ def created_at(self):
"""
- Starts this job on builder
+ Returns when this job was created
"""
- self.builder = builder
+ return self.data.created_at
- # Start to dispatch the build job
- self.state = "dispatching"
+ @property
+ def started_at(self):
+ """
+ Returns when this job was started
+ """
+ return self.data.started_at
- def running(self):
- self.state = "running"
+ @property
+ def finished_at(self):
+ """
+ Returns when this job finished
+ """
+ return self.data.finished_at
- # Set start time
- self.time_started = datetime.datetime.utcnow()
- self.time_finished = None
+ def start(self, builder):
+ """
+ Starts this job on builder
+ """
+ log.info("Starting job %s on %s" % (self, builder))
- def finished(self):
- self.state = "finished"
+ # Store the assigned builder
+ self._set_attribute("builder_id", builder)
- # Log end time
- self.time_finished = datetime.datetime.utcnow()
+ # Store the time
+ self._set_attribute_now("started_at")
- # Notify users
- self.send_finished_message()
+ def finished(self, success, message=None):
+ """
+ Called when this job has finished
+ """
+ # Store the time
+ self._set_attribute_now("finished_at")
- def failed(self, message):
- self.state = "failed"
- self.message = message
+ # XXX handle success status
- # Log end time
- self.time_finished = datetime.datetime.utcnow()
+ # Store message
+ self._set_attribute("message", message)
# Notify users
- self.send_failed_message()
+ if success:
+ self.send_finished_message()
+ else:
+ self._set_attribute("failed", True)
+ self.send_failed_message()
+
+ # XXX propagate any changes to the build
- def is_active(self):
+ def is_running(self):
"""
- Returns True if this job is active
+ Returns True if this job is running
"""
- return self.time_started and not self.time_finished
+ return self.started_at and not self.finished_at
def has_finished(self):
- if self.time_finished:
+ if self.finished_at:
return True
return False
- def has_failed(self):
- return self.state == "failed"
+ @property
+ def failed(self):
+ """
+ Indicates whether this job has failed
+ """
+ return self.data.failed
+
+ @property
+ def message(self):
+ return self.data.message
def delete(self):
"""
return entries
- def is_running(self):
- """
- Returns True if job is in a running state.
- """
- return self.state in ("pending", "dispatching", "running", "uploading")
-
- def get_state(self):
- return self.data.state
-
- def set_state(self, state):
- self._set_attribute("state", state)
+ # Builder
- # Automatically update the state of the build (not on test builds)
- if not self.test:
- self.build.auto_update_state()
-
- state = property(get_state, set_state)
-
- def set_message(self, message):
- if message:
- message = "%s" % message
-
- self._set_attribute("message", message)
-
- message = property(lambda s: s.data.message, set_message)
-
- def get_builder(self):
+ @lazy_property
+ def builder(self):
if self.data.builder_id:
return self.backend.builders.get_by_id(self.data.builder_id)
- def set_builder(self, builder, user=None):
- log.info("Builder %s has been assigned to %s" % (builder.name, self.name))
-
- self._set_attribute("builder_id", builder.id)
-
- # Log the event.
- if user:
- self.log("builder_assigned", builder=builder, user=user)
-
- builder = lazy_property(get_builder, set_builder)
-
@property
def arch(self):
return self.data.arch
@property
def duration(self):
- if not self.time_started:
- return 0
-
- if self.time_finished:
- delta = self.time_finished - self.time_started
+ """
+ Returns the total build duration or elapsed time
+ """
+ if self.has_finished():
+ return self.finished_at - self.started_at
else:
- delta = datetime.datetime.utcnow() - self.time_started
-
- return delta.total_seconds()
-
- @property
- def time_created(self):
- return self.data.time_created
-
- def set_time_started(self, time_started):
- self._set_attribute("time_started", time_started)
-
- time_started = property(lambda s: s.data.time_started, set_time_started)
-
- def set_time_finished(self, time_finished):
- self._set_attribute("time_finished", time_finished)
-
- time_finished = property(lambda s: s.data.time_finished, set_time_finished)
-
- def set_start_not_before(self, start_not_before):
- self._set_attribute("start_not_before", start_not_before)
-
- start_not_before = property(lambda s: s.data.start_not_before, set_start_not_before)
-
- def get_pkg_by_uuid(self, uuid):
- pkg = self.backend.packages._get_package("SELECT packages.id FROM packages \
- JOIN jobs_packages ON jobs_packages.pkg_id = packages.id \
- WHERE jobs_packages.job_id = %s AND packages.uuid = %s",
- self.id, uuid)
-
- if pkg:
- pkg.job = self
- return pkg
+ return datetime.datetime.utcnow() - self.started_at
@lazy_property
def logfiles(self):
self.db.execute("INSERT INTO jobs_packages(job_id, pkg_id) VALUES(%s, %s)",
self.id, pkg.id)
- def get_aborted_state(self):
- return self.data.aborted_state
-
- def set_aborted_state(self, state):
- self._set_attribute("aborted_state", state)
-
- aborted_state = property(get_aborted_state, set_aborted_state)
-
@property
def message_recipients(self):
l = []
ALTER SEQUENCE public.builds_id_seq OWNED BY public.builds.id;
---
--- Name: jobs; Type: TABLE; Schema: public; Owner: pakfire
---
-
-CREATE TABLE public.jobs (
- id integer NOT NULL,
- uuid uuid DEFAULT gen_random_uuid() NOT NULL,
- build_id integer NOT NULL,
- state text DEFAULT 'pending'::text NOT NULL,
- arch text NOT NULL,
- time_created timestamp without time zone DEFAULT now() NOT NULL,
- time_started timestamp without time zone,
- time_finished timestamp without time zone,
- start_not_before timestamp without time zone,
- builder_id integer,
- aborted_state integer DEFAULT 0 NOT NULL,
- message text,
- test boolean DEFAULT true NOT NULL,
- superseeded_by integer,
- dependency_check_succeeded boolean,
- dependency_check_at timestamp without time zone,
- CONSTRAINT jobs_states CHECK ((state = ANY (ARRAY['pending'::text, 'dispatching'::text, 'running'::text, 'uploading'::text, 'finished'::text, 'aborted'::text, 'download_error'::text, 'failed'::text])))
-);
-
-
-ALTER TABLE public.jobs OWNER TO pakfire;
-
--
-- Name: builds_watchers; Type: TABLE; Schema: public; Owner: pakfire
--
ALTER SEQUENCE public.images_types_id_seq OWNED BY public.images_types.id;
+--
+-- Name: jobs; Type: TABLE; Schema: public; Owner: pakfire
+--
+
+CREATE TABLE public.jobs (
+ id integer NOT NULL,
+ uuid uuid DEFAULT gen_random_uuid() NOT NULL,
+ build_id integer NOT NULL,
+ arch text NOT NULL,
+ created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ started_at timestamp without time zone,
+ finished_at timestamp without time zone,
+ builder_id integer,
+ message text,
+ test boolean DEFAULT true NOT NULL,
+ superseeded_by integer,
+ dependency_check_succeeded boolean,
+ dependency_check_at timestamp without time zone,
+ deleted boolean DEFAULT false NOT NULL,
+ failed boolean DEFAULT false NOT NULL
+);
+
+
+ALTER TABLE public.jobs OWNER TO pakfire;
+
--
-- Name: job_queue; Type: VIEW; Schema: public; Owner: pakfire
--
CREATE VIEW public.job_queue AS
SELECT jobs.id AS job_id,
- rank() OVER (ORDER BY (NOT jobs.test), builds.priority DESC, jobs.time_created) AS rank,
+ rank() OVER (ORDER BY (NOT jobs.test), builds.priority DESC, jobs.created_at) AS rank,
jobs.arch
FROM (public.jobs
LEFT JOIN public.builds ON ((jobs.build_id = builds.id)))
- WHERE ((jobs.state = 'pending'::text) AND (jobs.dependency_check_succeeded IS TRUE));
+ WHERE ((jobs.deleted IS FALSE) AND (jobs.started_at IS NULL) AND (jobs.finished_at IS NULL) AND (jobs.dependency_check_succeeded IS TRUE));
ALTER TABLE public.job_queue OWNER TO pakfire;
CREATE VIEW public.package_estimated_build_times AS
SELECT packages.name,
jobs.arch,
- avg((jobs.time_finished - jobs.time_started)) AS build_time
+ avg((jobs.finished_at - jobs.started_at)) AS build_time
FROM ((public.jobs
LEFT JOIN public.builds ON ((jobs.build_id = builds.id)))
LEFT JOIN public.packages ON ((builds.pkg_id = packages.id)))
- WHERE ((jobs.state = 'finished'::text) AND (jobs.test IS FALSE) AND (jobs.time_started IS NOT NULL) AND (jobs.time_finished IS NOT NULL))
+ WHERE ((jobs.deleted IS FALSE) AND (jobs.started_at IS NOT NULL) AND (jobs.finished_at IS NOT NULL) AND (jobs.failed IS FALSE) AND (jobs.test IS FALSE))
GROUP BY packages.name, jobs.arch;
ADD CONSTRAINT idx_2198057_primary PRIMARY KEY (id);
---
--- Name: jobs idx_2198063_primary; Type: CONSTRAINT; Schema: public; Owner: pakfire
---
-
-ALTER TABLE ONLY public.jobs
- ADD CONSTRAINT idx_2198063_primary PRIMARY KEY (id);
-
-
--
-- Name: jobs_packages idx_2198085_primary; Type: CONSTRAINT; Schema: public; Owner: pakfire
--
ADD CONSTRAINT jobs_packages_unique UNIQUE (job_id, pkg_id);
+--
+-- Name: jobs jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: pakfire
+--
+
+ALTER TABLE ONLY public.jobs
+ ADD CONSTRAINT jobs_pkey PRIMARY KEY (id);
+
+
--
-- Name: mirrors_checks mirrors_checks_pkey; Type: CONSTRAINT; Schema: public; Owner: pakfire
--
CREATE INDEX idx_2198018_user_id ON public.builds_comments USING btree (user_id);
---
--- Name: idx_2198063_build_id; Type: INDEX; Schema: public; Owner: pakfire
---
-
-CREATE INDEX idx_2198063_build_id ON public.jobs USING btree (build_id);
-
-
---
--- Name: idx_2198063_state; Type: INDEX; Schema: public; Owner: pakfire
---
-
-CREATE INDEX idx_2198063_state ON public.jobs USING btree (state);
-
-
---
--- Name: idx_2198063_uuid; Type: INDEX; Schema: public; Owner: pakfire
---
-
-CREATE UNIQUE INDEX idx_2198063_uuid ON public.jobs USING btree (uuid);
-
-
--
-- Name: idx_2198080_job_id; Type: INDEX; Schema: public; Owner: pakfire
--
--
--- Name: jobs_builders_active_jobs; Type: INDEX; Schema: public; Owner: pakfire
+-- Name: jobs_build_id; Type: INDEX; Schema: public; Owner: pakfire
--
-CREATE INDEX jobs_builders_active_jobs ON public.jobs USING btree (builder_id) WHERE (state = ANY (ARRAY['dispatching'::text, 'running'::text]));
+CREATE INDEX jobs_build_id ON public.jobs USING btree (build_id) WHERE (deleted IS FALSE);
--
--
--- Name: jobs_queue_ready; Type: INDEX; Schema: public; Owner: pakfire
+-- Name: jobs_finished_at; Type: INDEX; Schema: public; Owner: pakfire
+--
+
+CREATE INDEX jobs_finished_at ON public.jobs USING btree (finished_at DESC) WHERE (finished_at IS NOT NULL);
+
+
+--
+-- Name: jobs_pending; Type: INDEX; Schema: public; Owner: pakfire
--
-CREATE INDEX jobs_queue_ready ON public.jobs USING btree (id) WHERE ((state = 'new'::text) AND (dependency_check_succeeded IS TRUE));
+CREATE INDEX jobs_pending ON public.jobs USING btree (id) WHERE ((deleted IS FALSE) AND (started_at IS NULL) AND (finished_at IS NULL) AND (dependency_check_succeeded IS TRUE));
--
--- Name: jobs_time_finished; Type: INDEX; Schema: public; Owner: pakfire
+-- Name: jobs_started_at; Type: INDEX; Schema: public; Owner: pakfire
--
-CREATE INDEX jobs_time_finished ON public.jobs USING btree (time_finished DESC) WHERE (time_finished IS NOT NULL);
+CREATE INDEX jobs_started_at ON public.jobs USING btree (started_at) WHERE ((started_at IS NOT NULL) AND (finished_at IS NULL));
--
--- Name: jobs_time_started; Type: INDEX; Schema: public; Owner: pakfire
+-- Name: jobs_uuid; Type: INDEX; Schema: public; Owner: pakfire
--
-CREATE INDEX jobs_time_started ON public.jobs USING btree (time_started) WHERE ((time_started IS NOT NULL) AND (time_finished IS NULL));
+CREATE UNIQUE INDEX jobs_uuid ON public.jobs USING btree (uuid) WHERE (deleted IS FALSE);
--
{% for job in jobs %}
- <div class="callout small {% if job.has_failed() %}alert{% end %}">
+ <div class="callout small {% if job.failed %}alert{% end %}">
<div class="grid-x grid-padding-x align-middle">
<div class="cell medium-5">
<h6>
{# Show all packages that have been built #}
{% if show_packages and job.packages %}
<ul>
- {% for package in job %}
+ {% for package in job.packages %}
<li>
<a href="/package/{{ package.uuid }}">
{{ package }}
<div class="cell medium-7">
<div class="menu vertical align-right">
- {% if job.is_active() %}
+ {% if job.is_running() %}
<li class="menu-text">
{{ format_time(job.duration, shorter=True) }}
</li>
{% elif job.has_finished() %}
<li class="menu-text">
{{ _("Finished %s") % \
- locale.format_date(job.time_finished, shorter=True) }}
+ locale.format_date(job.finished_at, shorter=True) }}
</li>
{% end %}
jobs = []
# Get all active jobs
- jobs += self.backend.jobs.get_active()
+ jobs += self.backend.jobs.running
# Get some recently finished jobs
jobs += self.backend.jobs.get_recently_ended(limit=12)