'Django - Replication lag

Prerequisites:

class Task(models.Model):
    class TaskStatus(models.TextChoices):
        HIDDEN = 'hidden', _('hidden')
        NEW = 'new', _('new')
        IN_PROGRESS = 'in_progress', _('in progress')
        DONE = 'done', _('done')
        FAILED = 'failed', _('failed')
        PLANNED = 'planned', _('planned')
        REVOKED = 'revoked', _('revoked')

    title = models.CharField(max_length=500)
    performer = models.ForeignKey(User, on_delete=models.CASCADE)
    status = models.CharField(choices=TaskStatus.choices, default=TaskStatus.HIDDEN, max_length=25)


class TaskTransition(models.Model):
    task = models.ForeignKey('tasks.Task', on_delete=models.CASCADE, related_name='transitions')
    actor = models.ForeignKey(User, on_delete=models.RESTRICT, related_name='transitions')
    old_status = models.CharField(choices=TaskStatus.choices, max_length=25)
    new_status = models.CharField(choices=TaskStatus.choices, max_length=25)


class TaskView(ModelViewSet):
    queryset = Task.objects.all()
    serializer = TaskSerializer


class CreateTaskTransitionView(mixins.RetrieveModelMixin, generics.CreateAPIView):
    serializer_class = CreateTaskTransitionSerializer


class CreateTaskTransitionSerializer(serializers.ModelSerializer):
    task = serializers.HiddenField(default=HiddenTaskField())
    actor = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = TaskTransition
        fields = '__all__'

    def validate(self, attrs):
        # allowed status transitions:
        # hidden > new
        # new > in_progress, failed
        # in_progress > done, failed
        return attrs

    def save(self, **kwargs):
        task = self.validated_data['task']
        new_status = self.validated_data['new_status']

        TaskTransition.objects.create(
            task=task,
            new_status=new_status,
            old_status=task.status,
            actor=self.validated_data['actor']
        )

        task.status = new_status
        task.save()

        return {}

We use two databases. First default for writing and replica for reading.

Because of validation as you can see only one unique task transition can be done. But sometimes it happens that there can be multiple same transitions. For example 5 transitions in_progress > done.

And when retrieve task instance task is with not actual status.

It's obvious that this happens because of replication lag: in CreateTaskTransitionSerializer task is read from replica with not updated (actual) status.

So I thought that to solve this problem retrieve action of TaskView should read from default, right? If so how can I do it? Somethings like this:

class TaskView(models.Model):
    def get_queryset(self):
        qs = super().get_queryset(request)
        if self.action == 'retrieve':
             qs = qs.using('default') 
        return qs

?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source