From 72c8e18044628e3fd3a887f8f9c2484723d78b60 Mon Sep 17 00:00:00 2001 From: simon987 Date: Sun, 5 May 2019 19:09:45 -0400 Subject: [PATCH] Add API endpoint to pause a worker --- api/main.go | 1 + api/models.go | 5 + api/task.go | 8 ++ api/worker.go | 48 +++++++++ schema.sql | 229 ++++++++++++++++++++-------------------- storage/worker.go | 5 +- test/api_worker_test.go | 84 ++++++++++++++- test/schema.sql | 229 ++++++++++++++++++++-------------------- 8 files changed, 378 insertions(+), 231 deletions(-) diff --git a/api/main.go b/api/main.go index 97c3bf3..8a232eb 100644 --- a/api/main.go +++ b/api/main.go @@ -86,6 +86,7 @@ func New() *WebAPI { api.router.POST("/worker/create", LogRequestMiddleware(api.CreateWorker)) api.router.POST("/worker/update", LogRequestMiddleware(api.UpdateWorker)) + api.router.POST("/worker/set_paused", LogRequestMiddleware(api.WorkerSetPaused)) api.router.GET("/worker/get/:id", LogRequestMiddleware(api.GetWorker)) api.router.GET("/worker/stats", LogRequestMiddleware(api.GetAllWorkerStats)) diff --git a/api/models.go b/api/models.go index d41041c..efc253f 100644 --- a/api/models.go +++ b/api/models.go @@ -269,6 +269,11 @@ type UpdateWorkerRequest struct { Alias string `json:"alias"` } +type WorkerSetPausedRequest struct { + Worker int64 `json:"worker"` + Paused bool `json:"paused"` +} + type CreateWorkerRequest struct { Alias string `json:"alias"` } diff --git a/api/task.go b/api/task.go index d481e1f..623386f 100644 --- a/api/task.go +++ b/api/task.go @@ -200,6 +200,14 @@ func (api *WebAPI) GetTaskFromProject(r *Request) { return } + if worker.Paused { + r.Json(JsonResponse{ + Ok: false, + Message: "A manager has paused you", + }, 400) + return + } + project, err := strconv.ParseInt(r.Ctx.UserValue("project").(string), 10, 64) if err != nil || project <= 0 { r.Json(JsonResponse{ diff --git a/api/worker.go b/api/worker.go index 69ba291..38b8ec5 100644 --- a/api/worker.go +++ b/api/worker.go @@ -126,6 +126,54 @@ func (api *WebAPI) UpdateWorker(r *Request) { } } +func (api *WebAPI) WorkerSetPaused(r *Request) { + + sess := api.Session.StartFasthttp(r.Ctx) + manager := sess.Get("manager") + + if manager == nil || !manager.(*storage.Manager).WebsiteAdmin { + r.Json(JsonResponse{ + Ok: false, + Message: "Unauthorized", + }, 403) + return + } + + req := &WorkerSetPausedRequest{} + err := json.Unmarshal(r.Ctx.Request.Body(), req) + if err != nil { + r.Json(JsonResponse{ + Ok: false, + Message: "Could not parse request", + }, 400) + return + } + + worker := api.Database.GetWorker(req.Worker) + if worker == nil { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker", + }, 400) + return + } + + worker.Paused = req.Paused + + ok := api.Database.UpdateWorker(worker) + + if ok { + r.OkJson(JsonResponse{ + Ok: true, + }) + } else { + r.OkJson(JsonResponse{ + Ok: false, + Message: "Could not update worker", + }) + } +} + func (api *WebAPI) GetAllWorkerStats(r *Request) { stats := api.Database.GetAllWorkerStats() diff --git a/schema.sql b/schema.sql index ebfb14c..a4115e7 100755 --- a/schema.sql +++ b/schema.sql @@ -1,179 +1,180 @@ DROP TABLE IF EXISTS worker, project, task, log_entry, - worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, - worker_verifies_task; + worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, + worker_verifies_task; CREATE TABLE worker ( - id SERIAL PRIMARY KEY NOT NULL, - alias TEXT NOT NULL, - created INTEGER NOT NULL, - secret BYTEA NOT NULL, - closed_task_count INTEGER DEFAULT 0 NOT NULL + id SERIAL PRIMARY KEY NOT NULL, + alias TEXT NOT NULL, + created INTEGER NOT NULL, + secret BYTEA NOT NULL, + closed_task_count INTEGER DEFAULT 0 NOT NULL, + paused boolean NOT NULL DEFAULT false ); CREATE TABLE project ( - id SERIAL PRIMARY KEY NOT NULL, - priority INTEGER DEFAULT 0 NOT NULL, - closed_task_count INT DEFAULT 0 NOT NULL, - chain INT DEFAULT NULL REFERENCES project (id), - public boolean NOT NULL, - hidden boolean NOT NULL, - paused boolean NOT NULL, - name TEXT UNIQUE NOT NULL, - clone_url TEXT NOT NULL, - git_repo TEXT NOT NULL, - version TEXT NOT NULL, - motd TEXT NOT NULL, - secret TEXT NOT NULL DEFAULT '{}', - webhook_secret TEXT NOT NULL, - assign_rate DOUBLE PRECISION NOT NULL, - submit_rate DOUBLE PRECISION NOT NULL + id SERIAL PRIMARY KEY NOT NULL, + priority INTEGER DEFAULT 0 NOT NULL, + closed_task_count INT DEFAULT 0 NOT NULL, + chain INT DEFAULT NULL REFERENCES project (id), + public boolean NOT NULL, + hidden boolean NOT NULL, + paused boolean NOT NULL, + name TEXT UNIQUE NOT NULL, + clone_url TEXT NOT NULL, + git_repo TEXT NOT NULL, + version TEXT NOT NULL, + motd TEXT NOT NULL, + secret TEXT NOT NULL DEFAULT '{}', + webhook_secret TEXT NOT NULL, + assign_rate DOUBLE PRECISION NOT NULL, + submit_rate DOUBLE PRECISION NOT NULL ); CREATE TABLE worker_access ( - worker INTEGER REFERENCES worker (id), - project INTEGER REFERENCES project (id), - role_assign boolean, - role_submit boolean, - request boolean, - primary key (worker, project) + worker INTEGER REFERENCES worker (id), + project INTEGER REFERENCES project (id), + role_assign boolean, + role_submit boolean, + request boolean, + primary key (worker, project) ); CREATE TABLE task ( - hash64 BIGINT DEFAULT NULL, - id SERIAL PRIMARY KEY, - project INTEGER REFERENCES project (id), - assignee INTEGER REFERENCES worker (id), - max_assign_time INTEGER DEFAULT 0, - assign_time INTEGER DEFAULT NULL, - verification_count INTEGER DEFAULT 0, - priority SMALLINT DEFAULT 0, - retries SMALLINT DEFAULT 0, - max_retries SMALLINT, - status SMALLINT DEFAULT 1, - recipe TEXT, - UNIQUE (project, hash64) + hash64 BIGINT DEFAULT NULL, + id SERIAL PRIMARY KEY, + project INTEGER REFERENCES project (id), + assignee INTEGER REFERENCES worker (id), + max_assign_time INTEGER DEFAULT 0, + assign_time INTEGER DEFAULT NULL, + verification_count INTEGER DEFAULT 0, + priority SMALLINT DEFAULT 0, + retries SMALLINT DEFAULT 0, + max_retries SMALLINT, + status SMALLINT DEFAULT 1, + recipe TEXT, + UNIQUE (project, hash64) ); CREATE TABLE worker_verifies_task ( - verification_hash BIGINT NOT NULL, - task BIGINT REFERENCES task (id) ON DELETE CASCADE NOT NULL, - worker INT REFERENCES worker (id) NOT NULL + verification_hash BIGINT NOT NULL, + task BIGINT REFERENCES task (id) ON DELETE CASCADE NOT NULL, + worker INT REFERENCES worker (id) NOT NULL ); CREATE TABLE log_entry ( - level INTEGER NOT NULL, - message TEXT NOT NULL, - message_data TEXT NOT NULL, - timestamp INTEGER NOT NULL + level INTEGER NOT NULL, + message TEXT NOT NULL, + message_data TEXT NOT NULL, + timestamp INTEGER NOT NULL ); CREATE TABLE manager ( - id SERIAL PRIMARY KEY, - register_time INTEGER NOT NULL, - tracker_admin BOOLEAN NOT NULL, - username TEXT UNIQUE NOT NULL, - password BYTEA NOT NULL + id SERIAL PRIMARY KEY, + register_time INTEGER NOT NULL, + tracker_admin BOOLEAN NOT NULL, + username TEXT UNIQUE NOT NULL, + password BYTEA NOT NULL ); CREATE TABLE manager_has_role_on_project ( - manager INTEGER REFERENCES manager (id) NOT NULL, - role SMALLINT NOT NULL, - project INTEGER REFERENCES project (id) NOT NULL, - PRIMARY KEY (manager, project) + manager INTEGER REFERENCES manager (id) NOT NULL, + role SMALLINT NOT NULL, + project INTEGER REFERENCES project (id) NOT NULL, + PRIMARY KEY (manager, project) ); CREATE TABLE project_monitoring_snapshot ( - project INT REFERENCES project (id) NOT NULL, - new_task_count INT NOT NULL, - failed_task_count INT NOT NULL, - closed_task_count INT NOT NULL, - awaiting_verification_task_count INT NOT NULL, - worker_access_count INT NOT NULL, - timestamp INT NOT NULL + project INT REFERENCES project (id) NOT NULL, + new_task_count INT NOT NULL, + failed_task_count INT NOT NULL, + closed_task_count INT NOT NULL, + awaiting_verification_task_count INT NOT NULL, + worker_access_count INT NOT NULL, + timestamp INT NOT NULL ); CREATE OR REPLACE FUNCTION on_task_delete_proc() RETURNS TRIGGER AS $$ DECLARE - chain INTEGER; + chain INTEGER; BEGIN - if OLD.assignee IS NOT NULL THEN - UPDATE project - SET closed_task_count=closed_task_count + 1 - WHERE id = OLD.project returning project.chain into chain; - UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; - IF chain != 0 THEN - INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, - priority, retries, max_retries, status, recipe) - VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, - old.verification_count, old.priority, 0, old.max_retries, 1, - old.recipe) - ON CONFLICT DO NOTHING; + if OLD.assignee IS NOT NULL THEN + UPDATE project + SET closed_task_count=closed_task_count + 1 + WHERE id = OLD.project returning project.chain into chain; + UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; + IF chain != 0 THEN + INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, + priority, retries, max_retries, status, recipe) + VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, + old.verification_count, old.priority, 0, old.max_retries, 1, + old.recipe) + ON CONFLICT DO NOTHING; + end if; end if; - end if; - RETURN OLD; + RETURN OLD; END; $$ LANGUAGE 'plpgsql'; CREATE TRIGGER on_task_delete - BEFORE DELETE - ON task - FOR EACH ROW + BEFORE DELETE + ON task + FOR EACH ROW EXECUTE PROCEDURE on_task_delete_proc(); CREATE OR REPLACE FUNCTION on_manager_insert() RETURNS TRIGGER AS $$ BEGIN - IF NEW.id = 1 THEN - UPDATE manager SET tracker_admin= TRUE WHERE id = 1; - end if; - RETURN NEW; + IF NEW.id = 1 THEN + UPDATE manager SET tracker_admin= TRUE WHERE id = 1; + end if; + RETURN NEW; END; $$ LANGUAGE 'plpgsql'; CREATE TRIGGER on_manager_insert - AFTER INSERT - ON manager - FOR EACH ROW + AFTER INSERT + ON manager + FOR EACH ROW EXECUTE PROCEDURE on_manager_insert(); CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver BIGINT) RETURNS BOOLEAN AS $$ DECLARE - res INT = NULL; + res INT = NULL; BEGIN - DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res; - - IF res IS NULL THEN - INSERT INTO worker_verifies_task (worker, verification_hash, task) - SELECT wid, ver, task.id - FROM task - WHERE assignee = wid - AND task.id = tid; - - DELETE - FROM task - WHERE id = tid - AND assignee = wid - AND (SELECT COUNT(*) as vcnt - FROM worker_verifies_task wvt - WHERE task = tid - GROUP BY wvt.verification_hash - ORDER BY vcnt DESC - LIMIT 1) >= task.verification_count RETURNING task.id INTO res; + DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res; IF res IS NULL THEN - UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid; - end if; - end if; + INSERT INTO worker_verifies_task (worker, verification_hash, task) + SELECT wid, ver, task.id + FROM task + WHERE assignee = wid + AND task.id = tid; - RETURN res IS NOT NULL; + DELETE + FROM task + WHERE id = tid + AND assignee = wid + AND (SELECT COUNT(*) as vcnt + FROM worker_verifies_task wvt + WHERE task = tid + GROUP BY wvt.verification_hash + ORDER BY vcnt DESC + LIMIT 1) >= task.verification_count RETURNING task.id INTO res; + + IF res IS NULL THEN + UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid; + end if; + end if; + + RETURN res IS NOT NULL; END; $$ LANGUAGE 'plpgsql'; diff --git a/storage/worker.go b/storage/worker.go index ada0135..df4977f 100644 --- a/storage/worker.go +++ b/storage/worker.go @@ -9,6 +9,7 @@ type Worker struct { Created int64 `json:"created"` Alias string `json:"alias,omitempty"` Secret []byte `json:"secret"` + Paused bool `json:"paused"` } type WorkerStats struct { @@ -96,8 +97,8 @@ func (database *Database) GrantAccess(workerId int64, projectId int64) bool { func (database *Database) UpdateWorker(worker *Worker) bool { db := database.getDB() - res, err := db.Exec(`UPDATE worker SET alias=$1 WHERE id=$2`, - worker.Alias, worker.Id) + res, err := db.Exec(`UPDATE worker SET alias=$1, paused=$2 WHERE id=$3`, + worker.Alias, worker.Paused, worker.Id) handleErr(err) rowsAffected, _ := res.RowsAffected() diff --git a/test/api_worker_test.go b/test/api_worker_test.go index 2f62e83..be7e45d 100644 --- a/test/api_worker_test.go +++ b/test/api_worker_test.go @@ -6,6 +6,7 @@ import ( "github.com/simon987/task_tracker/client" "github.com/simon987/task_tracker/storage" "net/http" + "strings" "testing" ) @@ -29,6 +30,10 @@ func TestCreateGetWorker(t *testing.T) { if w.Alias != "my_worker_alias" { t.Error() } + + if w.Paused != false { + t.Error() + } } func TestGetWorkerNotFound(t *testing.T) { @@ -68,6 +73,10 @@ func TestUpdateAliasValid(t *testing.T) { if w.Alias != "new alias" { t.Error() } + + if w.Paused != false { + t.Error() + } } func TestCreateWorkerAliasInvalid(t *testing.T) { @@ -109,6 +118,74 @@ func TestInvalidAccessRequest(t *testing.T) { } } +func TestAssignTaskWhenPaused(t *testing.T) { + + w := genWid() + + pid := createProjectAsAdmin(api.CreateProjectRequest{ + Name: "testassigntaskwhenpaused", + CloneUrl: "testassigntaskwhenpaused", + GitRepo: "testassigntaskwhenpaused", + }).Content.Id + + requestAccess(api.CreateWorkerAccessRequest{ + Submit: true, + Assign: true, + Project: pid, + }, w) + acceptAccessRequest(pid, w.Id, testAdminCtx) + + r := createTask(api.SubmitTaskRequest{ + Project: pid, + Recipe: "a", + Hash64: 1, + }, w) + + if r.Ok != true { + t.Error() + } + + pauseWorker(&api.WorkerSetPausedRequest{ + Paused: true, + Worker: w.Id, + }, testAdminCtx) + + resp := getTaskFromProject(pid, w) + + if resp.Ok != false { + t.Error() + } + if !strings.Contains(resp.Message, "paused") { + t.Error() + } +} + +func TestPauseInvalidWorker(t *testing.T) { + + r := pauseWorker(&api.WorkerSetPausedRequest{ + Paused: true, + Worker: 9999111, + }, testAdminCtx) + + if r.Ok != false { + t.Error() + } +} + +func TestPauseUnauthorized(t *testing.T) { + + w := genWid() + + r := pauseWorker(&api.WorkerSetPausedRequest{ + Paused: true, + Worker: w.Id, + }, testUserCtx) + + if r.Ok != false { + t.Error() + } +} + func createWorker(req api.CreateWorkerRequest) (ar client.CreateWorkerResponse) { r := Post("/worker/create", req, nil, nil) UnmarshalResponse(r, &ar) @@ -147,8 +224,13 @@ func rejectAccessRequest(pid int64, wid int64, s *http.Client) (ar api.JsonRespo } func updateWorker(request api.UpdateWorkerRequest, w *storage.Worker) (ar api.JsonResponse) { - r := Post("/worker/update", request, w, nil) UnmarshalResponse(r, &ar) return } + +func pauseWorker(request *api.WorkerSetPausedRequest, s *http.Client) (ar api.JsonResponse) { + r := Post("/worker/set_paused", request, nil, s) + UnmarshalResponse(r, &ar) + return +} diff --git a/test/schema.sql b/test/schema.sql index 5385159..57b14fd 100755 --- a/test/schema.sql +++ b/test/schema.sql @@ -1,180 +1,181 @@ DROP TABLE IF EXISTS worker, project, task, log_entry, - worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, - worker_verifies_task; + worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, + worker_verifies_task; CREATE TABLE worker ( - id SERIAL PRIMARY KEY NOT NULL, - alias TEXT NOT NULL, - created INTEGER NOT NULL, - secret BYTEA NOT NULL, - closed_task_count INTEGER DEFAULT 0 NOT NULL + id SERIAL PRIMARY KEY NOT NULL, + alias TEXT NOT NULL, + created INTEGER NOT NULL, + secret BYTEA NOT NULL, + closed_task_count INTEGER DEFAULT 0 NOT NULL, + paused boolean NOT NULL DEFAULT false ); CREATE TABLE project ( - id SERIAL PRIMARY KEY NOT NULL, - priority INTEGER DEFAULT 0 NOT NULL, - closed_task_count INT DEFAULT 0 NOT NULL, - chain INT DEFAULT NULL REFERENCES project (id), - public boolean NOT NULL, - hidden boolean NOT NULL, - paused boolean NOT NULL, - name TEXT UNIQUE NOT NULL, - clone_url TEXT NOT NULL, - git_repo TEXT NOT NULL, - version TEXT NOT NULL, - motd TEXT NOT NULL, - secret TEXT NOT NULL DEFAULT '{}', - webhook_secret TEXT NOT NULL, - assign_rate DOUBLE PRECISION NOT NULL, - submit_rate DOUBLE PRECISION NOT NULL + id SERIAL PRIMARY KEY NOT NULL, + priority INTEGER DEFAULT 0 NOT NULL, + closed_task_count INT DEFAULT 0 NOT NULL, + chain INT DEFAULT NULL REFERENCES project (id), + public boolean NOT NULL, + hidden boolean NOT NULL, + paused boolean NOT NULL, + name TEXT UNIQUE NOT NULL, + clone_url TEXT NOT NULL, + git_repo TEXT NOT NULL, + version TEXT NOT NULL, + motd TEXT NOT NULL, + secret TEXT NOT NULL DEFAULT '{}', + webhook_secret TEXT NOT NULL, + assign_rate DOUBLE PRECISION NOT NULL, + submit_rate DOUBLE PRECISION NOT NULL ); CREATE TABLE worker_access ( - worker INTEGER REFERENCES worker (id), - project INTEGER REFERENCES project (id), - role_assign boolean, - role_submit boolean, - request boolean, - primary key (worker, project) + worker INTEGER REFERENCES worker (id), + project INTEGER REFERENCES project (id), + role_assign boolean, + role_submit boolean, + request boolean, + primary key (worker, project) ); CREATE TABLE task ( - hash64 BIGINT DEFAULT NULL, - id SERIAL PRIMARY KEY, - project INTEGER REFERENCES project (id), - assignee INTEGER REFERENCES worker (id), - max_assign_time INTEGER DEFAULT 0, - assign_time INTEGER DEFAULT NULL, - verification_count INTEGER DEFAULT 0, - priority SMALLINT DEFAULT 0, - retries SMALLINT DEFAULT 0, - max_retries SMALLINT, - status SMALLINT DEFAULT 1, - recipe TEXT, - UNIQUE (project, hash64) + hash64 BIGINT DEFAULT NULL, + id SERIAL PRIMARY KEY, + project INTEGER REFERENCES project (id), + assignee INTEGER REFERENCES worker (id), + max_assign_time INTEGER DEFAULT 0, + assign_time INTEGER DEFAULT NULL, + verification_count INTEGER DEFAULT 0, + priority SMALLINT DEFAULT 0, + retries SMALLINT DEFAULT 0, + max_retries SMALLINT, + status SMALLINT DEFAULT 1, + recipe TEXT, + UNIQUE (project, hash64) ); CREATE TABLE worker_verifies_task ( - verification_hash BIGINT NOT NULL, - task BIGINT REFERENCES task (id) ON DELETE CASCADE NOT NULL, - worker INT REFERENCES worker (id) NOT NULL + verification_hash BIGINT NOT NULL, + task BIGINT REFERENCES task (id) ON DELETE CASCADE NOT NULL, + worker INT REFERENCES worker (id) NOT NULL ); CREATE TABLE log_entry ( - level INTEGER NOT NULL, - message TEXT NOT NULL, - message_data TEXT NOT NULL, - timestamp INTEGER NOT NULL + level INTEGER NOT NULL, + message TEXT NOT NULL, + message_data TEXT NOT NULL, + timestamp INTEGER NOT NULL ); CREATE TABLE manager ( - id SERIAL PRIMARY KEY, - register_time INTEGER NOT NULL, - tracker_admin BOOLEAN NOT NULL, - username TEXT UNIQUE NOT NULL, - password BYTEA NOT NULL + id SERIAL PRIMARY KEY, + register_time INTEGER NOT NULL, + tracker_admin BOOLEAN NOT NULL, + username TEXT UNIQUE NOT NULL, + password BYTEA NOT NULL ); CREATE TABLE manager_has_role_on_project ( - manager INTEGER REFERENCES manager (id) NOT NULL, - role SMALLINT NOT NULL, - project INTEGER REFERENCES project (id) NOT NULL, - PRIMARY KEY (manager, project) + manager INTEGER REFERENCES manager (id) NOT NULL, + role SMALLINT NOT NULL, + project INTEGER REFERENCES project (id) NOT NULL, + PRIMARY KEY (manager, project) ); CREATE TABLE project_monitoring_snapshot ( - project INT REFERENCES project (id) NOT NULL, - new_task_count INT NOT NULL, - failed_task_count INT NOT NULL, - closed_task_count INT NOT NULL, - awaiting_verification_task_count INT NOT NULL, - worker_access_count INT NOT NULL, - timestamp INT NOT NULL + project INT REFERENCES project (id) NOT NULL, + new_task_count INT NOT NULL, + failed_task_count INT NOT NULL, + closed_task_count INT NOT NULL, + awaiting_verification_task_count INT NOT NULL, + worker_access_count INT NOT NULL, + timestamp INT NOT NULL ); CREATE OR REPLACE FUNCTION on_task_delete_proc() RETURNS TRIGGER AS $$ DECLARE - chain INTEGER; + chain INTEGER; BEGIN - if OLD.assignee IS NOT NULL THEN - UPDATE project - SET closed_task_count=closed_task_count + 1 - WHERE id = OLD.project returning project.chain into chain; - UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; - IF chain != 0 THEN - INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, - priority, retries, max_retries, status, recipe) - VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, - old.verification_count, old.priority, 0, old.max_retries, 1, - old.recipe) - ON CONFLICT DO NOTHING; + if OLD.assignee IS NOT NULL THEN + UPDATE project + SET closed_task_count=closed_task_count + 1 + WHERE id = OLD.project returning project.chain into chain; + UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; + IF chain != 0 THEN + INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, + priority, retries, max_retries, status, recipe) + VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, + old.verification_count, old.priority, 0, old.max_retries, 1, + old.recipe) + ON CONFLICT DO NOTHING; + end if; end if; - end if; - RETURN OLD; + RETURN OLD; END; $$ LANGUAGE 'plpgsql'; CREATE TRIGGER on_task_delete - BEFORE DELETE - ON task - FOR EACH ROW + BEFORE DELETE + ON task + FOR EACH ROW EXECUTE PROCEDURE on_task_delete_proc(); CREATE OR REPLACE FUNCTION on_manager_insert() RETURNS TRIGGER AS $$ BEGIN - IF NEW.id = 1 THEN - UPDATE manager SET tracker_admin= TRUE WHERE id = 1; - end if; - RETURN NEW; + IF NEW.id = 1 THEN + UPDATE manager SET tracker_admin= TRUE WHERE id = 1; + end if; + RETURN NEW; END; $$ LANGUAGE 'plpgsql'; CREATE TRIGGER on_manager_insert - AFTER INSERT - ON manager - FOR EACH ROW + AFTER INSERT + ON manager + FOR EACH ROW EXECUTE PROCEDURE on_manager_insert(); CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver BIGINT) RETURNS BOOLEAN AS $$ DECLARE - res INT = NULL; + res INT = NULL; BEGIN - DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res; - - IF res IS NULL THEN - INSERT INTO worker_verifies_task (worker, verification_hash, task) - SELECT wid, ver, task.id - FROM task - WHERE assignee = wid - AND task.id = tid; - - DELETE - FROM task - WHERE id = tid - AND assignee = wid - AND (SELECT COUNT(*) as vcnt - FROM worker_verifies_task wvt - WHERE task = tid - GROUP BY wvt.verification_hash - ORDER BY vcnt DESC - LIMIT 1) >= task.verification_count RETURNING task.id INTO res; + DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res; IF res IS NULL THEN - UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid; - end if; - end if; + INSERT INTO worker_verifies_task (worker, verification_hash, task) + SELECT wid, ver, task.id + FROM task + WHERE assignee = wid + AND task.id = tid; - RETURN res IS NOT NULL; + DELETE + FROM task + WHERE id = tid + AND assignee = wid + AND (SELECT COUNT(*) as vcnt + FROM worker_verifies_task wvt + WHERE task = tid + GROUP BY wvt.verification_hash + ORDER BY vcnt DESC + LIMIT 1) >= task.verification_count RETURNING task.id INTO res; + + IF res IS NULL THEN + UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid; + end if; + end if; + + RETURN res IS NOT NULL; END; $$ LANGUAGE 'plpgsql';