Browse Source

fix(server): Fix O(n^2) runtime for adding many references

Julius Pfrommer 4 years ago
parent
commit
ef93d2d801

+ 1 - 1
CMakeLists.txt

@@ -560,6 +560,7 @@ set(exported_headers ${exported_headers}
                      ${PROJECT_SOURCE_DIR}/include/open62541/plugin/securitypolicy.h
                      ${PROJECT_SOURCE_DIR}/include/open62541/server_pubsub.h
                      ${PROJECT_SOURCE_DIR}/include/open62541/plugin/pubsub.h
+                     ${PROJECT_SOURCE_DIR}/deps/ziptree.h
                      ${PROJECT_SOURCE_DIR}/include/open62541/plugin/nodestore.h
                      ${historizing_exported_headers}
                      ${PROJECT_SOURCE_DIR}/include/open62541/server_config.h
@@ -570,7 +571,6 @@ set(exported_headers ${exported_headers}
                      ${PROJECT_SOURCE_DIR}/include/open62541/client_highlevel_async.h)
 
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/open62541_queue.h
-                     ${PROJECT_SOURCE_DIR}/deps/ziptree.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/deps/libc_time.h
                      ${PROJECT_SOURCE_DIR}/deps/base64.h

+ 15 - 2
include/open62541/plugin/nodestore.h

@@ -18,6 +18,7 @@
  * / OPC UA services to interact with the information model. */
 
 #include <open62541/server.h>
+#include "ziptree.h"
 
 _UA_BEGIN_DECLS
 
@@ -65,12 +66,24 @@ struct UA_MonitoredItem;
  * not known or not important. The ``nodeClass`` attribute is used to ensure the
  * correctness of casting from ``UA_Node`` to a specific node type. */
 
+/* Ordered tree structure for fast member check */
+typedef struct UA_ReferenceTarget {
+    ZIP_ENTRY(UA_ReferenceTarget) zipfields;
+    UA_UInt32 targetHash; /* Hash of the target nodeid */
+    UA_ExpandedNodeId target;
+} UA_ReferenceTarget;
+
+ZIP_HEAD(UA_ReferenceTargetHead, UA_ReferenceTarget);
+typedef struct UA_ReferenceTargetHead UA_ReferenceTargetHead;
+ZIP_PROTTYPE(UA_ReferenceTargetHead, UA_ReferenceTarget, UA_ReferenceTarget)
+
 /* List of reference targets with the same reference type and direction */
 typedef struct {
     UA_NodeId referenceTypeId;
     UA_Boolean isInverse;
-    size_t targetIdsSize;
-    UA_ExpandedNodeId *targetIds;
+    size_t refTargetsSize;
+    UA_ReferenceTarget *refTargets;
+    UA_ReferenceTargetHead refTargetsTree;
 } UA_NodeReferenceKind;
 
 #define UA_NODE_BASEATTRIBUTES                  \

+ 131 - 59
src/server/ua_nodes.c

@@ -17,6 +17,20 @@
 /* There is no UA_Node_new() method here. Creating nodes is part of the
  * NodeStore layer */
 
+static enum ZIP_CMP
+cmpRefTarget(const void *a, const void *b) {
+    const UA_ReferenceTarget *aa = (const UA_ReferenceTarget*)a;
+    const UA_ReferenceTarget *bb = (const UA_ReferenceTarget*)b;
+    if(aa->targetHash < bb->targetHash)
+        return ZIP_CMP_LESS;
+    if(aa->targetHash > bb->targetHash)
+        return ZIP_CMP_MORE;
+    return (enum ZIP_CMP)UA_ExpandedNodeId_order(&aa->target, &bb->target);
+}
+
+ZIP_IMPL(UA_ReferenceTargetHead, UA_ReferenceTarget, zipfields,
+         UA_ReferenceTarget, zipfields, cmpRefTarget)
+
 void UA_Node_deleteMembers(UA_Node *node) {
     /* Delete standard content */
     UA_NodeId_deleteMembers(&node->nodeId);
@@ -176,16 +190,39 @@ UA_Node_copy(const UA_Node *src, UA_Node *dst) {
             UA_NodeReferenceKind *srefs = &src->references[i];
             UA_NodeReferenceKind *drefs = &dst->references[i];
             drefs->isInverse = srefs->isInverse;
+            ZIP_INIT(&drefs->refTargetsTree);
             retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId);
             if(retval != UA_STATUSCODE_GOOD)
                 break;
-            retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize,
-                                    (void**)&drefs->targetIds,
-                                    &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
+            drefs->refTargets = (UA_ReferenceTarget*)
+                UA_malloc(srefs->refTargetsSize* sizeof(UA_ReferenceTarget));
+            if(!drefs->refTargets) {
+                UA_NodeId_deleteMembers(&drefs->referenceTypeId);
+                break;
+            }
+            uintptr_t arraydiff = (uintptr_t)drefs->refTargets - (uintptr_t)srefs->refTargets;
+            for(size_t j = 0; j < srefs->refTargetsSize; j++) {
+                retval |= UA_ExpandedNodeId_copy(&srefs->refTargets[j].target,
+                                                 &drefs->refTargets[j].target);
+                drefs->refTargets[j].targetHash = srefs->refTargets[j].targetHash;
+                drefs->refTargets[j].zipfields.zip_right = NULL;
+                if(srefs->refTargets[j].zipfields.zip_right)
+                    *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left =
+                        (uintptr_t)srefs->refTargets[j].zipfields.zip_right + arraydiff;
+                drefs->refTargets[j].zipfields.zip_left = NULL;
+                if(srefs->refTargets[j].zipfields.zip_left)
+                    *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left =
+                        (uintptr_t)srefs->refTargets[j].zipfields.zip_left + arraydiff;
+            }
+            srefs->refTargetsTree.zip_root = NULL;
+            if(drefs->refTargetsTree.zip_root)
+                *(uintptr_t*)&drefs->refTargetsTree.zip_root =
+                    (uintptr_t)srefs->refTargetsTree.zip_root + arraydiff;
+            drefs->refTargetsSize= srefs->refTargetsSize;
             if(retval != UA_STATUSCODE_GOOD)
                 break;
-            drefs->targetIdsSize = srefs->targetIdsSize;
         }
+
         if(retval != UA_STATUSCODE_GOOD) {
             UA_Node_deleteMembers(dst);
             return retval;
@@ -453,75 +490,100 @@ UA_Node_setAttributes(UA_Node *node, const void *attributes,
 /*********************/
 
 static UA_StatusCode
-addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target) {
-    UA_ExpandedNodeId *targets =
-        (UA_ExpandedNodeId*) UA_realloc(refs->targetIds,
-                                        sizeof(UA_ExpandedNodeId) * (refs->targetIdsSize+1));
+addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target,
+                   UA_UInt32 targetHash) {
+    UA_ReferenceTarget *targets = (UA_ReferenceTarget*)
+        UA_realloc(refs->refTargets, (refs->refTargetsSize + 1) * sizeof(UA_ReferenceTarget));
     if(!targets)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    refs->targetIds = targets;
-    UA_StatusCode retval =
-        UA_ExpandedNodeId_copy(target, &refs->targetIds[refs->targetIdsSize]);
-
-    if(retval == UA_STATUSCODE_GOOD) {
-        refs->targetIdsSize++;
-    } else if(refs->targetIdsSize == 0) {
-        /* We had zero references before (realloc was a malloc) */
-        UA_free(refs->targetIds);
-        refs->targetIds = NULL;
+    /* Repair the pointers in the tree for the realloced array */
+    uintptr_t arraydiff = (uintptr_t)targets - (uintptr_t)refs->refTargets;
+    if(arraydiff != 0) {
+        for(size_t i = 0; i < refs->refTargetsSize; i++) {
+            if(targets[i].zipfields.zip_left)
+                *(uintptr_t*)&targets[i].zipfields.zip_left += arraydiff;
+            if(targets[i].zipfields.zip_right)
+                *(uintptr_t*)&targets[i].zipfields.zip_right += arraydiff;
+        }
     }
-    return retval;
+
+    if(refs->refTargetsTree.zip_root)
+        *(uintptr_t*)&refs->refTargetsTree.zip_root += arraydiff;
+    refs->refTargets = targets;
+
+    UA_ReferenceTarget *entry = &refs->refTargets[refs->refTargetsSize];
+    UA_StatusCode retval = UA_ExpandedNodeId_copy(target, &entry->target);
+    if(retval != UA_STATUSCODE_GOOD) {
+        if(refs->refTargetsSize== 0) {
+            /* We had zero references before (realloc was a malloc) */
+            UA_free(refs->refTargets);
+            refs->refTargets = NULL;
+        }
+        return retval;
+    }
+
+    entry->targetHash = targetHash;
+    ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree,
+               entry, ZIP_FFS32(UA_UInt32_random()));
+    refs->refTargetsSize++;
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
 addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) {
-    UA_NodeReferenceKind *refs =
-        (UA_NodeReferenceKind*)UA_realloc(node->references,
-                                          sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
+    UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)
+        UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
     if(!refs)
         return UA_STATUSCODE_BADOUTOFMEMORY;
     node->references = refs;
     UA_NodeReferenceKind *newRef = &refs[node->referencesSize];
     memset(newRef, 0, sizeof(UA_NodeReferenceKind));
 
+    ZIP_INIT(&newRef->refTargetsTree);
     newRef->isInverse = !item->isForward;
     UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &newRef->referenceTypeId);
-    retval |= addReferenceTarget(newRef, &item->targetNodeId);
+    UA_UInt32 targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId);
+    retval |= addReferenceTarget(newRef, &item->targetNodeId, targetHash);
 
-    if(retval == UA_STATUSCODE_GOOD) {
-        node->referencesSize++;
-    } else {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_NodeId_deleteMembers(&newRef->referenceTypeId);
         if(node->referencesSize == 0) {
             UA_free(node->references);
             node->references = NULL;
         }
+        return retval;
     }
-    return retval;
+
+    node->referencesSize++;
+    return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode
 UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
+    /* Find the matching refkind */
     UA_NodeReferenceKind *existingRefs = NULL;
     for(size_t i = 0; i < node->referencesSize; ++i) {
         UA_NodeReferenceKind *refs = &node->references[i];
-        if(refs->isInverse != item->isForward
-                && UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) {
+        if(refs->isInverse != item->isForward &&
+           UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) {
             existingRefs = refs;
             break;
         }
     }
-    if(existingRefs != NULL) {
-        for(size_t i = 0; i < existingRefs->targetIdsSize; i++) {
-            if(UA_ExpandedNodeId_equal(&existingRefs->targetIds[i],
-                                       &item->targetNodeId)) {
-                return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED;
-            }
-        }
-        return addReferenceTarget(existingRefs, &item->targetNodeId);
-    }
-    return addReferenceKind(node, item);
+
+    if(!existingRefs)
+        return addReferenceKind(node, item);
+
+    UA_ReferenceTarget tmpTarget;
+    tmpTarget.target = item->targetNodeId;
+    tmpTarget.targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId);
+
+    UA_ReferenceTarget *found =
+        ZIP_FIND(UA_ReferenceTargetHead, &existingRefs->refTargetsTree, &tmpTarget);
+    if(found)
+        return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED;
+    return addReferenceTarget(existingRefs, &item->targetNodeId, tmpTarget.targetHash);
 }
 
 UA_StatusCode
@@ -533,33 +595,40 @@ UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item) {
         if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId))
             continue;
 
-        for(size_t j = refs->targetIdsSize; j > 0; --j) {
-            UA_ExpandedNodeId *target = &refs->targetIds[j-1];
-            if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->nodeId))
+        for(size_t j = refs->refTargetsSize; j > 0; --j) {
+            UA_ReferenceTarget *target = &refs->refTargets[j-1];
+            if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->target.nodeId))
                 continue;
 
             /* Ok, delete the reference */
-            UA_ExpandedNodeId_deleteMembers(target);
-            refs->targetIdsSize--;
+            ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree, target);
+            UA_ExpandedNodeId_deleteMembers(&target->target);
+            refs->refTargetsSize--;
 
             /* One matching target remaining */
-            if(refs->targetIdsSize > 0) {
-                if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
-                                               // and destination overlap in
-                                               // memcpy
-                    *target = refs->targetIds[refs->targetIdsSize];
+            if(refs->refTargetsSize > 0) {
+                if(j-1 != refs->refTargetsSize) {
+                    /* avoid valgrind error: Source and destination overlap in
+                     * memcpy */
+                    ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree,
+                               &refs->refTargets[refs->refTargetsSize]);
+                    *target = refs->refTargets[refs->refTargetsSize];
+                    ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree,
+                               target, ZIP_RANK(target, zipfields));
+                }
                 return UA_STATUSCODE_GOOD;
             }
 
             /* No target for the ReferenceType remaining. Remove entry. */
-            UA_free(refs->targetIds);
+            UA_free(refs->refTargets);
             UA_NodeId_deleteMembers(&refs->referenceTypeId);
             node->referencesSize--;
             if(node->referencesSize > 0) {
-                if(i-1 != node->referencesSize) // avoid valgrind error: Source
-                                                // and destination overlap in
-                                                // memcpy
+                if(i-1 != node->referencesSize) {
+                    /* avoid valgrind error: Source and destination overlap in
+                     * memcpy */
                     node->references[i-1] = node->references[node->referencesSize];
+                }
                 return UA_STATUSCODE_GOOD;
             }
 
@@ -594,7 +663,9 @@ UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize,
             continue;
 
         /* Remove references */
-        UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
+        for(size_t j = 0; j < refs->refTargetsSize; j++)
+            UA_ExpandedNodeId_deleteMembers(&refs->refTargets[j].target);
+        UA_free(refs->refTargets);
         UA_NodeId_deleteMembers(&refs->referenceTypeId);
         node->referencesSize--;
 
@@ -604,17 +675,18 @@ UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize,
         node->references[i-1] = node->references[node->referencesSize];
     }
 
-    if(node->referencesSize == 0) {
-        /* The array is empty. Remove. */
-        UA_free(node->references);
-        node->references = NULL;
-    } else {
+    if(node->referencesSize > 0) {
         /* Realloc to save memory */
         UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)
             UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * node->referencesSize);
         if(refs) /* Do nothing if realloc fails */
             node->references = refs;
+        return;
     }
+
+    /* The array is empty. Remove. */
+    UA_free(node->references);
+    node->references = NULL;
 }
 
 void UA_Node_deleteReferences(UA_Node *node) {

+ 2 - 2
src/server/ua_server.c

@@ -132,8 +132,8 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = parentCopy->referencesSize; i > 0; --i) {
         UA_NodeReferenceKind *ref = &parentCopy->references[i - 1];
-        for(size_t j = 0; j<ref->targetIdsSize; j++) {
-            retval = callback(ref->targetIds[j].nodeId, ref->isInverse,
+        for(size_t j = 0; j<ref->refTargetsSize; j++) {
+            retval = callback(ref->refTargets[j].target.nodeId, ref->isInverse,
                               ref->referenceTypeId, handle);
             if(retval != UA_STATUSCODE_GOOD)
                 goto cleanup;

+ 7 - 7
src/server/ua_server_utils.c

@@ -57,7 +57,7 @@ isNodeInTreeNoCircular(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *
             continue;
 
         /* Match the targets or recurse */
-        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
+        for(size_t j = 0; j < refs->refTargetsSize; ++j) {
             /* Check if we already have seen the referenced node and skip to
              * avoid endless recursion. Do this only at every 5th depth to save
              * effort. Circular dependencies are rare and forbidden for most
@@ -66,7 +66,7 @@ isNodeInTreeNoCircular(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *
                 struct ref_history *last = visitedRefs;
                 UA_Boolean skip = false;
                 while(!skip && last) {
-                    if(UA_NodeId_equal(last->id, &refs->targetIds[j].nodeId))
+                    if(UA_NodeId_equal(last->id, &refs->refTargets[j].target.nodeId))
                         skip = true;
                     last = last->parent;
                 }
@@ -75,13 +75,13 @@ isNodeInTreeNoCircular(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *
             }
 
             /* Stack-allocate the visitedRefs structure for the next depth */
-            struct ref_history nextVisitedRefs = {visitedRefs, &refs->targetIds[j].nodeId,
+            struct ref_history nextVisitedRefs = {visitedRefs, &refs->refTargets[j].target.nodeId,
                                                   (UA_UInt16)(visitedRefs->depth+1)};
 
             /* Recurse */
             UA_Boolean foundRecursive =
-                isNodeInTreeNoCircular(nsCtx, &refs->targetIds[j].nodeId, nodeToFind, &nextVisitedRefs,
-                                       referenceTypeIds, referenceTypeIdsSize);
+                isNodeInTreeNoCircular(nsCtx, &refs->refTargets[j].target.nodeId, nodeToFind,
+                                       &nextVisitedRefs, referenceTypeIds, referenceTypeIdsSize);
             if(foundRecursive) {
                 UA_Nodestore_releaseNode(nsCtx, node);
                 return true;
@@ -136,8 +136,8 @@ getNodeType(UA_Server *server, const UA_Node *node) {
             continue;
         if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef))
             continue;
-        UA_assert(node->references[i].targetIdsSize > 0);
-        const UA_NodeId *targetId = &node->references[i].targetIds[0].nodeId;
+        UA_assert(node->references[i].refTargetsSize> 0);
+        const UA_NodeId *targetId = &node->references[i].refTargets[0].target.nodeId;
         const UA_Node *type = UA_Nodestore_getNode(server->nsCtx, targetId);
         if(!type)
             continue;

+ 4 - 4
src/server/ua_services_method.c

@@ -30,9 +30,9 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
         if(!UA_NodeId_equal(&hasProperty, &rk->referenceTypeId))
             continue;
 
-        for(size_t j = 0; j < rk->targetIdsSize; ++j) {
+        for(size_t j = 0; j < rk->refTargetsSize; ++j) {
             const UA_Node *refTarget =
-                UA_Nodestore_getNode(server->nsCtx, &rk->targetIds[j].nodeId);
+                UA_Nodestore_getNode(server->nsCtx, &rk->refTargets[j].target.nodeId);
             if(!refTarget)
                 continue;
             if(refTarget->nodeClass == UA_NODECLASS_VARIABLE &&
@@ -147,8 +147,8 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
         if(!isNodeInTree(server->nsCtx, &rk->referenceTypeId,
                          &hasComponentNodeId, &hasSubTypeNodeId, 1))
             continue;
-        for(size_t j = 0; j < rk->targetIdsSize; ++j) {
-            if(UA_NodeId_equal(&rk->targetIds[j].nodeId, &request->methodId)) {
+        for(size_t j = 0; j < rk->refTargetsSize; ++j) {
+            if(UA_NodeId_equal(&rk->refTargets[j].target.nodeId, &request->methodId)) {
                 found = true;
                 break;
             }

+ 5 - 5
src/server/ua_services_nodemanagement.c

@@ -400,8 +400,8 @@ isMandatoryChild(UA_Server *server, UA_Session *session,
             continue;
         if(refs->isInverse)
             continue;
-        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
-            if(UA_NodeId_equal(&mandatoryId, &refs->targetIds[j].nodeId)) {
+        for(size_t j = 0; j < refs->refTargetsSize; ++j) {
+            if(UA_NodeId_equal(&mandatoryId, &refs->refTargets[j].target.nodeId)) {
                 UA_Nodestore_releaseNode(server->nsCtx, child);
                 return true;
             }
@@ -1366,8 +1366,8 @@ removeIncomingReferences(UA_Server *server, UA_Session *session,
         UA_NodeReferenceKind *refs = &node->references[i];
         item.isForward = refs->isInverse;
         item.referenceTypeId = refs->referenceTypeId;
-        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
-            item.sourceNodeId = refs->targetIds[j].nodeId;
+        for(size_t j = 0; j < refs->refTargetsSize; ++j) {
+            item.sourceNodeId = refs->refTargets[j].target.nodeId;
             Operation_deleteReference(server, session, NULL, &item, &dummy);
         }
     }
@@ -1398,7 +1398,7 @@ multipleHierarchies(size_t hierarchicalRefsSize, UA_ExpandedNodeId *hierarchical
         if(!hierarchical)
             continue;
 
-        incomingRefs += k->targetIdsSize;
+        incomingRefs += k->refTargetsSize;
         if(incomingRefs > 1)
             return true;
     }

+ 10 - 9
src/server/ua_services_view.c

@@ -176,8 +176,8 @@ addRelevantReferences(UA_Server *server, RefTree *rt, const UA_NodeId *nodeId,
         if(!relevantReference(&rk->referenceTypeId, refTypesSize, refTypes))
             continue;
 
-        for(size_t k = 0; k < rk->targetIdsSize; k++) {
-            retval = RefTree_add(rt ,&rk->targetIds[k]);
+        for(size_t k = 0; k < rk->refTargetsSize; k++) {
+            retval = RefTree_add(rt, &rk->refTargets[k].target);
             if(retval != UA_STATUSCODE_GOOD)
                 goto cleanup;
         }
@@ -468,13 +468,14 @@ browseReferences(UA_Server *server, const UA_Node *node,
             continue;
 
         /* Loop over the targets */
-        for(; targetIndex < rk->targetIdsSize; ++targetIndex) {
+        for(; targetIndex < rk->refTargetsSize; ++targetIndex) {
             target = NULL;
 
             /* Get the node if it is not a remote reference */
-            if(rk->targetIds[targetIndex].serverIndex == 0 &&
-               rk->targetIds[targetIndex].namespaceUri.data == NULL) {
-                target = UA_Nodestore_getNode(server->nsCtx, &rk->targetIds[targetIndex].nodeId);
+            if(rk->refTargets[targetIndex].target.serverIndex == 0 &&
+               rk->refTargets[targetIndex].target.namespaceUri.data == NULL) {
+                target = UA_Nodestore_getNode(server->nsCtx,
+                                              &rk->refTargets[targetIndex].target.nodeId);
 
                 /* Test if the node class matches */
                 if(target && !matchClassMask(target, bd->nodeClassMask)) {
@@ -495,7 +496,7 @@ browseReferences(UA_Server *server, const UA_Node *node,
 
             /* Copy the node description. Target is on top of the stack */
             retval = addReferenceDescription(server, rr, rk, bd->resultMask,
-                                             &rk->targetIds[targetIndex], target);
+                                             &rk->refTargets[targetIndex].target, target);
             UA_Nodestore_releaseNode(server->nsCtx, target);
             if(retval != UA_STATUSCODE_GOOD)
                 return retval;
@@ -803,8 +804,8 @@ walkBrowsePathElementReferenceTargets(UA_BrowsePathResult *result, size_t *targe
                                       UA_NodeId **next, size_t *nextSize, size_t *nextCount,
                                       UA_UInt32 elemDepth, const UA_NodeReferenceKind *rk) {
     /* Loop over the targets */
-    for(size_t i = 0; i < rk->targetIdsSize; i++) {
-        UA_ExpandedNodeId *targetId = &rk->targetIds[i];
+    for(size_t i = 0; i < rk->refTargetsSize; i++) {
+        UA_ExpandedNodeId *targetId = &rk->refTargets[i].target;
 
         /* Does the reference point to an external server? Then add to the
          * targets with the right path depth. */