From 4f8753b6830482f4dce4cdbcf6143c9f971009c1 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 25 Sep 2022 00:36:37 +0200 Subject: [PATCH] Make sure to save in atomic transaction. Resovle #2657. --- evennia/contrib/rpg/traits/tests.py | 24 +++++++++++++++++++++++- evennia/utils/evtable.py | 2 -- evennia/utils/idmapper/models.py | 21 ++++++++++++--------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/evennia/contrib/rpg/traits/tests.py b/evennia/contrib/rpg/traits/tests.py index 612fd8840d..72c4814d11 100644 --- a/evennia/contrib/rpg/traits/tests.py +++ b/evennia/contrib/rpg/traits/tests.py @@ -7,9 +7,12 @@ Unit test module for Trait classes. """ from copy import copy + from anything import Something +from evennia.objects.objects import DefaultCharacter +from evennia.utils.test_resources import BaseEvenniaTestCase, EvenniaTest from mock import MagicMock, patch -from evennia.utils.test_resources import BaseEvenniaTestCase + from . import traits @@ -1040,3 +1043,22 @@ class TestTraitFields(BaseEvenniaTestCase): self.assertEqual(13, obj2.strength.value) self.assertEqual(20, obj.strength.value) + + +class TraitContribTestingChar(DefaultCharacter): + HP = traits.TraitProperty("health", trait_type="trait", value=5) + + +class TraitPropertyTestCase(EvenniaTest): + """ + Test atomic updating. + + """ + + character_typeclass = TraitContribTestingChar + + def test_round1(self): + self.char1.HP.value = 1 + + def test_round2(self): + self.char1.HP.value = 2 diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 314ceea555..75749a639b 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -1629,8 +1629,6 @@ class EvTable(object): htable = self.nrows excess = len(row) - wtable - print(" len(row):", len(row), "wtable:", wtable, "excess:", excess) - if excess > 0: # we need to add new empty columns to table empty_rows = ["" for _ in range(htable)] diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index 0a4dda299e..19c63e5fa8 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -7,19 +7,20 @@ leave caching unexpectedly (no use of WeakRefs). Also adds `cache_size()` for monitoring the size of the cache. """ +import gc import os import threading -import gc import time from weakref import WeakValueDictionary -from twisted.internet.reactor import callFromThread -from django.core.exceptions import ObjectDoesNotExist, FieldError -from django.db.models.signals import post_save + +from django.core.exceptions import FieldError, ObjectDoesNotExist from django.db.models.base import Model, ModelBase -from django.db.models.signals import pre_delete, post_migrate +from django.db.models.signals import post_migrate, post_save, pre_delete +from django.db.transaction import atomic from django.db.utils import DatabaseError from evennia.utils import logger from evennia.utils.utils import dbref, get_evennia_pids, to_str +from twisted.internet.reactor import callFromThread from .manager import SharedMemoryManager @@ -444,13 +445,15 @@ class SharedMemoryModel(Model, metaclass=SharedMemoryModelBase): if _IS_MAIN_THREAD: # in main thread - normal operation try: - super().save(*args, **kwargs) + with atomic(): + super().save(*args, **kwargs) except DatabaseError: # we handle the 'update_fields did not update any rows' error that # may happen due to timing issues with attributes ufields_removed = kwargs.pop("update_fields", None) if ufields_removed: - super().save(*args, **kwargs) + with atomic(): + super().save(*args, **kwargs) else: raise else: @@ -623,8 +626,8 @@ def conditional_flush(max_rmem, force=False): if ((now - LAST_FLUSH) < AUTO_FLUSH_MIN_INTERVAL) and not force: # too soon after last flush. logger.log_warn( - "Warning: Idmapper flush called more than " - "once in %s min interval. Check memory usage." % (AUTO_FLUSH_MIN_INTERVAL / 60.0) + "Warning: Idmapper flush called more than once in %s min interval. Check memory usage." + % (AUTO_FLUSH_MIN_INTERVAL / 60.0) ) return