def test_eventual_read_doesnt_update_memcache(self): entity_data = {"field1": "Apple", "comb1": 1, "comb2": "Cherry"} identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) instance.refresh_from_db() # Add to memcache (consistent Get) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) cache.clear() for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.all()[0] # Inconsistent read for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_save_caches_outside_transaction_only(self): entity_data = {"field1": "Apple", "comb1": 1, "comb2": "Cherry"} identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) instance.refresh_from_db() for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) instance.delete() for identifier in identifiers: self.assertIsNone(cache.get(identifier)) with transaction.atomic(): instance = CachingTestModel.objects.create(**entity_data) for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_transactional_save_wipes_the_cache_only_after_its_result_is_consistently_available( self): entity_data = { "field1": "old", } identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) instance.refresh_from_db() # Add to memcache (consistent Get) for identifier in identifiers: self.assertEqual("old", cache.get(identifier)["field1"]) @non_transactional def non_transactional_read(instance_pk): CachingTestModel.objects.get(pk=instance_pk) with transaction.atomic(): instance.field1 = "new" instance.save() non_transactional_read( instance.pk) # could potentially recache the old object for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_eventual_read_doesnt_update_memcache(self): entity_data = { "field1": "Apple", "comb1": 1, "comb2": "Cherry" } identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity(CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.create(id=222, **entity_data) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) cache.clear() for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.all()[0] # Inconsistent read for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_save_inside_transaction_evicts_cache(self): entity_data = {"field1": "Apple", "comb1": 1, "comb2": "Cherry"} identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) instance = CachingTestModel.objects.create(id=222, **entity_data) instance.refresh_from_db() # Adds to memcache (consistent Get) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) with transaction.atomic(): instance.field1 = "Banana" instance.save() # Make sure that altering inside the transaction evicted the item from the cache # and that a get then hits the datastore (which then in turn caches) with sleuth.watch( "google.appengine.api.datastore.Get") as datastore_get: for identifier in identifiers: self.assertIsNone(cache.get(identifier)) self.assertEqual( "Banana", CachingTestModel.objects.get(pk=instance.pk).field1) self.assertTrue(datastore_get.called)
def test_transactional_save_wipes_the_cache_only_after_its_result_is_consistently_available(self): entity_data = { "field1": "old", } identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222) ), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) for identifier in identifiers: self.assertEqual("old", cache.get(identifier)["field1"]) @non_transactional def non_transactional_read(instance_pk): CachingTestModel.objects.get(pk=instance_pk) with transaction.atomic(): instance.field1 = "new" instance.save() non_transactional_read(instance.pk) # could potentially recache the old object for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_save_wipes_entity_from_cache_inside_transaction(self): entity_data = { "field1": "Apple", "comb1": 1, "comb2": "Cherry" } identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity(CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) with transaction.atomic(): instance.save() for identifier in identifiers: self.assertIsNone(cache.get(identifier))
def test_save_inside_transaction_evicts_cache(self): entity_data = { "field1": "Apple", "comb1": 1, "comb2": "Cherry" } identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity(CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) instance = CachingTestModel.objects.create(id=222, **entity_data) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) with transaction.atomic(): instance.field1 = "Banana" instance.save() # Make sure that altering inside the transaction evicted the item from the cache # and that a get then hits the datastore (which then in turn caches) with sleuth.watch("google.appengine.api.datastore.Get") as datastore_get: for identifier in identifiers: self.assertIsNone(cache.get(identifier)) self.assertEqual("Banana", CachingTestModel.objects.get(pk=instance.pk).field1) self.assertTrue(datastore_get.called)
def test_consistent_read_updates_memcache_outside_transaction(self): entity_data = {"field1": "Apple", "comb1": 1, "comb2": "Cherry"} identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity(CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) instance = CachingTestModel.objects.create(id=222, **entity_data) instance.refresh_from_db() # Add to memcache (consistent Get) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) cache.clear() for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.get(id=222) # Consistent read for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier))
def test_consistent_read_updates_memcache_outside_transaction(self): entity_data = {"field1": "Apple", "comb1": 1, "comb2": "Cherry"} identifiers = caching._apply_namespace( unique_utils.unique_identifiers_from_entity( CachingTestModel, FakeEntity(entity_data, id=222)), DEFAULT_NAMESPACE, ) for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.create(id=222, **entity_data) for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier)) cache.clear() for identifier in identifiers: self.assertIsNone(cache.get(identifier)) CachingTestModel.objects.get(id=222) # Consistent read for identifier in identifiers: self.assertEqual(entity_data, cache.get(identifier))