'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 |
|---|
