Repository: vertexproject/synapse

Issue:
delTufo dictionary changed during iteration
```
cli> ask inet:fqdn=woowoowoowoo.com delnode(force=1)
oplog:
    lift (took:0) {'took': 0, 'sub': 0, 'add': 1, 'mnem': 'lift'}
    delnode (took:1) {'excinfo': {'errfile': '/home/autouser/git/synapse/synapse/cores/common.py', 'errline': 2037, 'err': 'RuntimeError', 'errmsg': 'dictionary changed size during iteration'}, 'took': 1, 'sub': 1, 'add': 0, 'mnem': 'delnode'}

options:
    limit = None
    uniq = 1

limits:
    lift = None
    time = None
    touch = None

(0 results)
```

Discussion:

Patch:
diff --git a/synapse/cores/common.py b/synapse/cores/common.py
index 29a17931e..69634180c 100644
--- a/synapse/cores/common.py
+++ b/synapse/cores/common.py
@@ -2036,7 +2036,7 @@ class Cortex(EventBus, DataModel, Runtime, Configable, s_ingest.IngestApi):
             self.delTufoDset(tufo, name)
 
         if self.caching:
-            for prop, valu in tufo[1].items():
+            for prop, valu in list(tufo[1].items()):
                 self._bumpTufoCache(tufo, prop, valu, None)
 
         iden = tufo[0]


Test patch:
diff --git a/synapse/tests/test_cortex.py b/synapse/tests/test_cortex.py
index 24707a38d..4e1eb7a64 100644
--- a/synapse/tests/test_cortex.py
+++ b/synapse/tests/test_cortex.py
@@ -1143,12 +1143,17 @@ class CortexTest(SynTest):
             tufs1 = core.getTufosByProp('foo:qwer', valu=10)
             tufs2 = core.getTufosByProp('foo:qwer', valu=11)
 
+            # Ensure we have cached the tufos we're deleting.
+            self.nn(core.cache_byiden.get(tufo0[0]))
+            self.nn(core.cache_byiden.get(tufo1[0]))
+
             self.eq(len(tufs0), 2)
             self.eq(len(tufs1), 2)
             self.eq(len(tufs2), 0)
 
+            # Delete an uncached object - here the tufo contents was cached
+            # during lifts but the object itself is a different tuple id()
             core.delTufo(tufo0)
-            #tufo2 = core.formTufoByProp('foo','lol', qwer=10)
 
             tufs0 = core.getTufosByProp('foo:qwer')
             tufs1 = core.getTufosByProp('foo:qwer', valu=10)
@@ -1158,6 +1163,11 @@ class CortexTest(SynTest):
             self.eq(len(tufs1), 1)
             self.eq(len(tufs2), 0)
 
+            # Delete an object which was actually cached during lift
+            core.delTufo(tufs0[0])
+            tufs0 = core.getTufosByProp('foo:qwer')
+            self.eq(len(tufs0), 0)
+
     def test_cortex_caching_atlimit(self):
 
         with s_cortex.openurl('ram://') as core:
diff --git a/synapse/tests/test_lib_storm.py b/synapse/tests/test_lib_storm.py
index 0c146c1c0..e4236c6b4 100644
--- a/synapse/tests/test_lib_storm.py
+++ b/synapse/tests/test_lib_storm.py
@@ -439,6 +439,15 @@ class StormTest(SynTest):
             core.eval('inet:ipv4=1.2.3.4 delnode(force=1)')
             self.none(core.getTufoByProp('inet:ipv4', 0x01020304))
 
+    def test_storm_delnode_caching(self):
+        with s_cortex.openurl('ram:///') as core:
+            core.setConfOpt('caching', True)
+            node = core.formTufoByProp('inet:ipv4', 0x01020304)
+            core.eval('inet:ipv4=1.2.3.4 delnode()')
+            self.nn(core.getTufoByProp('inet:ipv4', 0x01020304))
+            core.eval('inet:ipv4=1.2.3.4 delnode(force=1)')
+            self.none(core.getTufoByProp('inet:ipv4', 0x01020304))
+
     def test_storm_editmode(self):
         with s_cortex.openurl('ram:///') as core:
             node = core.formTufoByProp('inet:ipv4', 0x01020304)