Skip to content

Commit

Permalink
Add batching functionality to delete_nodes
Browse files Browse the repository at this point in the history
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 FreeOpcUa#1148
  • Loading branch information
Nebukadneza committed Oct 19, 2020
1 parent 583d970 commit aab581f
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 10 deletions.
4 changes: 2 additions & 2 deletions opcua/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,8 @@ def get_namespace_index(self, uri):
uries = self.get_namespace_array()
return uries.index(uri)

def delete_nodes(self, nodes, recursive=False):
return delete_nodes(self.uaclient, nodes, recursive)
def delete_nodes(self, nodes, recursive=False, batch=False):
return delete_nodes(self.uaclient, nodes, recursive, batch=batch)

def import_xml(self, path=None, xmlstring=None):
"""
Expand Down
28 changes: 22 additions & 6 deletions opcua/common/manage_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,12 @@ def _guess_datatype(variant):
return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name))


def delete_nodes(server, nodes, recursive=False, delete_target_references=True):
def delete_nodes(server, nodes, recursive=False, delete_target_references=True, batch=False):
"""
Delete specified nodes. Optionally delete recursively all nodes with a
downward hierachic references to the node
Delete specified nodes. Optionally recursively delete all nodes with a
downward hierachic references to the node.
`batch` can be False or a integer. If a integer is given, batch the nodes
discovered into multiple DeleteNodesRequests.
return the list of deleted node and the result
"""
nodestodelete = []
Expand All @@ -386,9 +388,23 @@ def delete_nodes(server, nodes, recursive=False, delete_target_references=True):
it.NodeId = mynode.nodeid
it.DeleteTargetReferences = delete_target_references
nodestodelete.append(it)
params = ua.DeleteNodesParameters()
params.NodesToDelete = nodestodelete
return nodes, server.delete_nodes(params)

if batch and isinstance(batch, int):
chunks = [
nodestodelete[i : i + batch]
for i in range(0, len(nodestodelete), batch)
]

results = []
for chunk in chunks:
params = ua.DeleteNodesParameters()
params.NodesToDelete = chunk
results.extend(server.delete_nodes(params))
return nodes, results
else:
params = ua.DeleteNodesParameters()
params.NodesToDelete = nodestodelete
return nodes, server.delete_nodes(params)


def _add_childs(nodes):
Expand Down
4 changes: 2 additions & 2 deletions opcua/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,8 @@ def export_xml_by_ns(self, path, namespaces=None):
nodes = get_nodes_of_namespace(self, namespaces)
self.export_xml(nodes, path)

def delete_nodes(self, nodes, recursive=False):
return delete_nodes(self.iserver.isession, nodes, recursive)
def delete_nodes(self, nodes, recursive=False, batch=False):
return delete_nodes(self.iserver.isession, nodes, recursive, batch=batch)

def historize_node_data_change(self, node, period=timedelta(days=7), count=0):
"""
Expand Down
24 changes: 24 additions & 0 deletions tests/tests_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,30 @@ def test_delete_nodes_recursive2(self):
with self.assertRaises(ua.UaStatusCodeError):
node.get_browse_name()

def test_delete_nodes_recursive_batching(self):
obj = self.opc.get_objects_node()
fold = obj.add_folder(2, "FolderToDeleteRoot")
nfold = fold
mynodes = []
for i in range(7):
nfold = fold.add_folder(2, "FolderToDeleteRoot")
var = fold.add_variable(2, "VarToDeleteR", 9.1)
var = fold.add_property(2, "ProToDeleteR", 9.1)
prop = fold.add_property(2, "ProToDeleteR", 9.1)
o = fold.add_object(3, "ObjToDeleteR")
o_var = o.add_variable(3, "VarToDeleteRR", 9.2)
o_prop = o.add_property(3, "PropToDeleteRR", 9.2)
mynodes.append(nfold)
mynodes.append(var)
mynodes.append(prop)
mynodes.append(o)
mynodes.append(o_var)
mynodes.append(o_prop)
self.opc.delete_nodes([fold], recursive=True, batch=2)
for node in mynodes:
with self.assertRaises(ua.UaStatusCodeError):
node.get_browse_name()

def test_delete_references(self):
newtype = self.opc.get_node(ua.ObjectIds.HierarchicalReferences).add_reference_type(0, "HasSuperSecretVariable")

Expand Down

0 comments on commit aab581f

Please sign in to comment.