Skip to content

Commit aab581f

Browse files
committed
Add batching functionality to delete_nodes
When `delete_nodes` is given a large amount of nodes, or is instructed to recursively delete a big structure, the connection between client and server can time out. To remedy this, allow the user to supply a `batch` parameter, which controls how many nodes are deleted in one request. closes #1148
1 parent 583d970 commit aab581f

File tree

4 files changed

+50
-10
lines changed

4 files changed

+50
-10
lines changed

opcua/client/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,8 @@ def get_namespace_index(self, uri):
586586
uries = self.get_namespace_array()
587587
return uries.index(uri)
588588

589-
def delete_nodes(self, nodes, recursive=False):
590-
return delete_nodes(self.uaclient, nodes, recursive)
589+
def delete_nodes(self, nodes, recursive=False, batch=False):
590+
return delete_nodes(self.uaclient, nodes, recursive, batch=batch)
591591

592592
def import_xml(self, path=None, xmlstring=None):
593593
"""

opcua/common/manage_nodes.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,12 @@ def _guess_datatype(variant):
372372
return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name))
373373

374374

375-
def delete_nodes(server, nodes, recursive=False, delete_target_references=True):
375+
def delete_nodes(server, nodes, recursive=False, delete_target_references=True, batch=False):
376376
"""
377-
Delete specified nodes. Optionally delete recursively all nodes with a
378-
downward hierachic references to the node
377+
Delete specified nodes. Optionally recursively delete all nodes with a
378+
downward hierachic references to the node.
379+
`batch` can be False or a integer. If a integer is given, batch the nodes
380+
discovered into multiple DeleteNodesRequests.
379381
return the list of deleted node and the result
380382
"""
381383
nodestodelete = []
@@ -386,9 +388,23 @@ def delete_nodes(server, nodes, recursive=False, delete_target_references=True):
386388
it.NodeId = mynode.nodeid
387389
it.DeleteTargetReferences = delete_target_references
388390
nodestodelete.append(it)
389-
params = ua.DeleteNodesParameters()
390-
params.NodesToDelete = nodestodelete
391-
return nodes, server.delete_nodes(params)
391+
392+
if batch and isinstance(batch, int):
393+
chunks = [
394+
nodestodelete[i : i + batch]
395+
for i in range(0, len(nodestodelete), batch)
396+
]
397+
398+
results = []
399+
for chunk in chunks:
400+
params = ua.DeleteNodesParameters()
401+
params.NodesToDelete = chunk
402+
results.extend(server.delete_nodes(params))
403+
return nodes, results
404+
else:
405+
params = ua.DeleteNodesParameters()
406+
params.NodesToDelete = nodestodelete
407+
return nodes, server.delete_nodes(params)
392408

393409

394410
def _add_childs(nodes):

opcua/server/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,8 @@ def export_xml_by_ns(self, path, namespaces=None):
541541
nodes = get_nodes_of_namespace(self, namespaces)
542542
self.export_xml(nodes, path)
543543

544-
def delete_nodes(self, nodes, recursive=False):
545-
return delete_nodes(self.iserver.isession, nodes, recursive)
544+
def delete_nodes(self, nodes, recursive=False, batch=False):
545+
return delete_nodes(self.iserver.isession, nodes, recursive, batch=batch)
546546

547547
def historize_node_data_change(self, node, period=timedelta(days=7), count=0):
548548
"""

tests/tests_common.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,30 @@ def test_delete_nodes_recursive2(self):
173173
with self.assertRaises(ua.UaStatusCodeError):
174174
node.get_browse_name()
175175

176+
def test_delete_nodes_recursive_batching(self):
177+
obj = self.opc.get_objects_node()
178+
fold = obj.add_folder(2, "FolderToDeleteRoot")
179+
nfold = fold
180+
mynodes = []
181+
for i in range(7):
182+
nfold = fold.add_folder(2, "FolderToDeleteRoot")
183+
var = fold.add_variable(2, "VarToDeleteR", 9.1)
184+
var = fold.add_property(2, "ProToDeleteR", 9.1)
185+
prop = fold.add_property(2, "ProToDeleteR", 9.1)
186+
o = fold.add_object(3, "ObjToDeleteR")
187+
o_var = o.add_variable(3, "VarToDeleteRR", 9.2)
188+
o_prop = o.add_property(3, "PropToDeleteRR", 9.2)
189+
mynodes.append(nfold)
190+
mynodes.append(var)
191+
mynodes.append(prop)
192+
mynodes.append(o)
193+
mynodes.append(o_var)
194+
mynodes.append(o_prop)
195+
self.opc.delete_nodes([fold], recursive=True, batch=2)
196+
for node in mynodes:
197+
with self.assertRaises(ua.UaStatusCodeError):
198+
node.get_browse_name()
199+
176200
def test_delete_references(self):
177201
newtype = self.opc.get_node(ua.ObjectIds.HierarchicalReferences).add_reference_type(0, "HasSuperSecretVariable")
178202

0 commit comments

Comments
 (0)