From 862446fa799dcce61845ef95478add15253e3bc6 Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Thu, 20 Oct 2022 12:21:10 +0200 Subject: [PATCH] Update WorkerActivity.updated using a trigger --- .../0061_workeractivity_updated_triggers.py | 50 +++++++++++++++++++ arkindex/process/models.py | 22 +++++++- arkindex/process/tests/test_workeractivity.py | 2 +- arkindex/project/settings.py | 1 + requirements.txt | 1 + 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 arkindex/process/migrations/0061_workeractivity_updated_triggers.py diff --git a/arkindex/process/migrations/0061_workeractivity_updated_triggers.py b/arkindex/process/migrations/0061_workeractivity_updated_triggers.py new file mode 100644 index 0000000000..65b6dd3877 --- /dev/null +++ b/arkindex/process/migrations/0061_workeractivity_updated_triggers.py @@ -0,0 +1,50 @@ +# Generated by Django 4.0.7 on 2022-10-19 16:05 + +from django.db import migrations, models + +import pgtrigger.compiler +import pgtrigger.migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('process', '0060_remove_repository_provider_name'), + ] + + operations = [ + migrations.AlterField( + model_name='workeractivity', + name='updated', + field=models.DateTimeField(auto_now_add=True), + ), + pgtrigger.migrations.AddTrigger( + model_name='workeractivity', + trigger=pgtrigger.compiler.Trigger( + name='update_workeractivity_updated', + sql=pgtrigger.compiler.UpsertTriggerSql( + func='NEW.updated = now(); RETURN NEW;', + hash='1e5a8fa0718f420e6cd4f2a31434cd39a9c9bc67', + operation='UPDATE', + pgid='pgtrigger_update_workeractivity_updated_f2812', + table='process_workeractivity', + when='BEFORE', + ) + ), + ), + pgtrigger.migrations.AddTrigger( + model_name='workeractivity', + trigger=pgtrigger.compiler.Trigger( + name='read_only_workeractivity_updated', + sql=pgtrigger.compiler.UpsertTriggerSql( + condition='WHEN (OLD."updated" IS DISTINCT FROM (NEW."updated"))', + func="RAISE EXCEPTION 'pgtrigger: Cannot update rows from % table', TG_TABLE_NAME;", + hash='6276c6971a1d2669659e407418e2db1fa7dc6965', + operation='UPDATE', + pgid='pgtrigger_read_only_workeractivity_updated_a80ab', + table='process_workeractivity', + when='BEFORE', + ) + ), + ), + ] diff --git a/arkindex/process/models.py b/arkindex/process/models.py index 7e49841faa..31f0e00801 100644 --- a/arkindex/process/models.py +++ b/arkindex/process/models.py @@ -14,6 +14,7 @@ from django.utils.functional import cached_property from enumfields import Enum, EnumField from rest_framework.exceptions import ValidationError +import pgtrigger from arkindex.documents.models import Element from arkindex.images.models import ImageServer from arkindex.process.managers import ActivityManager, CorpusWorkerVersionManager @@ -867,13 +868,20 @@ class WorkerActivityState(Enum): Error = 'error' -class WorkerActivity(IndexableModel): +class WorkerActivity(models.Model): """ Many-to-many relationship between Element and WorkerVersion Used to track the activity of a worker version among multiple elements """ # Using an UUID helps to execute SQL raw INSERT id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + created = models.DateTimeField(auto_now_add=True) + + # We will manually handle the timestamp update using a PostgreSQL trigger to avoid issues with bulk inserts/updates, + # and we still let Django handle setting the timestamp initially, so we use `auto_now_add` and not `auto_now`. + updated = models.DateTimeField(auto_now_add=True) + element = models.ForeignKey( 'documents.Element', on_delete=models.CASCADE, @@ -921,6 +929,18 @@ class WorkerActivity(IndexableModel): condition=Q(configuration__isnull=False), ) ] + triggers = [ + pgtrigger.Trigger( + name='update_workeractivity_updated', + operation=pgtrigger.Update, + when=pgtrigger.Before, + func='NEW.updated = now(); RETURN NEW;', + ), + pgtrigger.ReadOnly( + name='read_only_workeractivity_updated', + fields=['updated'], + ) + ] class CorpusWorkerVersion(models.Model): diff --git a/arkindex/process/tests/test_workeractivity.py b/arkindex/process/tests/test_workeractivity.py index 4eeba62d3f..e24f1ccde8 100644 --- a/arkindex/process/tests/test_workeractivity.py +++ b/arkindex/process/tests/test_workeractivity.py @@ -124,7 +124,7 @@ class TestWorkerActivity(FixtureTestCase): ) for user, status_code, requests_count in cases: self.activity.state = WorkerActivityState.Queued - self.activity.save() + self.activity.save(update_fields=['state']) if user: self.client.force_login(user) with self.assertNumQueries(requests_count): diff --git a/arkindex/project/settings.py b/arkindex/project/settings.py index 677b5c56d6..7e79e251b4 100644 --- a/arkindex/project/settings.py +++ b/arkindex/project/settings.py @@ -109,6 +109,7 @@ INSTALLED_APPS = [ 'corsheaders', 'django_rq', 'drf_spectacular', + 'pgtrigger', 'ponos', # Our apps diff --git a/requirements.txt b/requirements.txt index ff2bc33dff..072b799b5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ bleach==5.0.1 django-admin-hstore-widget==1.2.0 django-cors-headers==3.13.0 django-enumfields==2.1.1 +django-pgtrigger==4.6.0 django-rq==2.5.1 djangorestframework==3.12.4 drf-spectacular==0.18.2 -- GitLab