server/modules/selva/module/edge.c
int Edge_Add(
        RedisModuleCtx *ctx,
        struct SelvaModify_Hierarchy *hierarchy,
        unsigned constraint_id,
        const char *field_name_str,
        size_t field_name_len,
        struct SelvaModify_HierarchyNode *src_node,
        struct SelvaModify_HierarchyNode *dst_node) {
    const struct EdgeFieldConstraints *constraints = &hierarchy->edge_field_constraints;
    const struct EdgeFieldConstraint *constraint;
    struct EdgeField *src_edge_field = NULL;
    int err;

    assert(hierarchy);

    /*
     * Get src_edge_field
     */
    err = get_or_create_EdgeField(constraints, src_node, field_name_str, field_name_len, constraint_id, &src_edge_field);
    if (err) {
        return err;
    }

    if (Edge_Has(src_edge_field, dst_node)) {
        return SELVA_EEXIST;
    }

    constraint = src_edge_field->constraint;

    if (constraint->flags & EDGE_FIELD_CONSTRAINT_FLAG_SINGLE_REF) {
        int res;
        /*
         * single_ref allows only one edge to exist in the field.
         */
        res = Edge_ClearField(ctx, hierarchy, src_node, field_name_str, field_name_len);
        if (res < 0) {
            return res;
        }
    }

    insert_edge(src_edge_field, dst_node);

    err = 0; /* Just to be sure. */
    if (constraint->flags & EDGE_FIELD_CONSTRAINT_FLAG_BIDIRECTIONAL) {
        /*
         * This field is bidirectional and so we need to create an edge pointing back.
         */
        err = Edge_Add(ctx, hierarchy, EDGE_FIELD_CONSTRAINT_DYNAMIC,
                       constraint->bck_field_name_str, constraint->bck_field_name_len,
                       dst_node, src_node);
        if (err && err != SELVA_EEXIST) {
            Selva_NodeId dst_node_id;
            int err1; /* We must retain the original err. */

            /*
             * Ok so, this is a bit dumb but we break an infinite loop by
             * ignoring SELVA_EEXIST. It's terribly inefficient to attempt to
             * create the same edge again just to figure that both ends were
             * created already. This implementation also practically allows
             * multidirectional edges between two nodes.
             * The normal flow should be like this:
             * Edge_Add(src, dst) -> Edge_Add(dst, src) -> Edge_Add(src, dst) => SELVA_EEXIST
             */
            fprintf(stderr, "%s:%d: An error occurred while creating a bidirectional edge: %s\n",
                    __FILE__, __LINE__, getSelvaErrorStr(err));

            /*
             * In case of an error we can't actually rollback anymore but we can
             * delete the edge we just created, perhaps it's still better than
             * leaving a half-broken bidir edge.
             * Surely this can fail too if we are OOMing.
             */
            SelvaHierarchy_GetNodeId(dst_node_id, dst_node);
            err1 = Edge_Delete(ctx, hierarchy, src_edge_field, src_node, dst_node_id);
            if (err1 && err1 != SELVA_ENOENT) {
                fprintf(stderr, "%s:%d: Failed to remove the broken edge: %s\n",
                        __FILE__, __LINE__, getSelvaErrorStr(err1));
            }
        } else {
            /* We don't want to leak the SELVA_EEXIST. */
            err = 0;
        }
    }

    SelvaSubscriptions_InheritEdge(ctx, hierarchy, src_node, dst_node, field_name_str, field_name_len);
    /*
     * Note that the regular change events for edges are only sent by the modify
     * command. Therefore we expect we don't need to do anything here.
     */

    return err;
}
/* RFE Optimize by taking edgeField as an arg. */
RFE Optimize by taking edgeField as an arg.
RFE Optimize by taking edgeField as an arg .
int Edge_Add(
        RedisModuleCtx *ctx,
        struct SelvaModify_Hierarchy *hierarchy,
        unsigned constraint_id,
        const char *field_name_str,
        size_t field_name_len,
        struct SelvaModify_HierarchyNode *src_node,
        struct SelvaModify_HierarchyNode *dst_node) {
    const struct EdgeFieldConstraints *constraints = &hierarchy->edge_field_constraints;
    const struct EdgeFieldConstraint *constraint;
    struct EdgeField *src_edge_field = NULL;
    int err;
    assert(hierarchy);
    err = get_or_create_EdgeField(constraints, src_node, field_name_str, field_name_len, constraint_id, &src_edge_field);
    if (err) {
        return err;
    }
    if (Edge_Has(src_edge_field, dst_node)) {
        return SELVA_EEXIST;
    }
    constraint = src_edge_field->constraint;
    if (constraint->flags & EDGE_FIELD_CONSTRAINT_FLAG_SINGLE_REF) {
        int res;
        res = Edge_ClearField(ctx, hierarchy, src_node, field_name_str, field_name_len);
        if (res < 0) {
            return res;
        }
    }
    insert_edge(src_edge_field, dst_node);
    err = 0; 
    if (constraint->flags & EDGE_FIELD_CONSTRAINT_FLAG_BIDIRECTIONAL) {
        err = Edge_Add(ctx, hierarchy, EDGE_FIELD_CONSTRAINT_DYNAMIC,
                       constraint->bck_field_name_str, constraint->bck_field_name_len,
                       dst_node, src_node);
        if (err && err != SELVA_EEXIST) {
            Selva_NodeId dst_node_id;
            int err1; 
            fprintf(stderr, "%s:%d: An error occurred while creating a bidirectional edge: %s\n",
                    __FILE__, __LINE__, getSelvaErrorStr(err));
            SelvaHierarchy_GetNodeId(dst_node_id, dst_node);
            err1 = Edge_Delete(ctx, hierarchy, src_edge_field, src_node, dst_node_id);
            if (err1 && err1 != SELVA_ENOENT) {
                fprintf(stderr, "%s:%d: Failed to remove the broken edge: %s\n",
                        __FILE__, __LINE__, getSelvaErrorStr(err1));
            }
        } else {
            err = 0;
        }
    }
    SelvaSubscriptions_InheritEdge(ctx, hierarchy, src_node, dst_node, field_name_str, field_name_len);
    return err;
}
int Edge_Add ( RedisModuleCtx * ctx , struct SelvaModify_Hierarchy * hierarchy , unsigned constraint_id , const char * field_name_str , size_t field_name_len , struct SelvaModify_HierarchyNode * src_node , struct SelvaModify_HierarchyNode * dst_node ) { const struct EdgeFieldConstraints * constraints = & hierarchy -> edge_field_constraints ; const struct EdgeFieldConstraint * constraint ; struct EdgeField * src_edge_field = NULL ; int err ; assert ( hierarchy ) ; err = get_or_create_EdgeField ( constraints , src_node , field_name_str , field_name_len , constraint_id , & src_edge_field ) ; if ( err ) { return err ; } if ( Edge_Has ( src_edge_field , dst_node ) ) { return SELVA_EEXIST ; } constraint = src_edge_field -> constraint ; if ( constraint -> flags & EDGE_FIELD_CONSTRAINT_FLAG_SINGLE_REF ) { int res ; res = Edge_ClearField ( ctx , hierarchy , src_node , field_name_str , field_name_len ) ; if ( res < 0 ) { return res ; } } insert_edge ( src_edge_field , dst_node ) ; err = 0 ; if ( constraint -> flags & EDGE_FIELD_CONSTRAINT_FLAG_BIDIRECTIONAL ) { err = Edge_Add ( ctx , hierarchy , EDGE_FIELD_CONSTRAINT_DYNAMIC , constraint -> bck_field_name_str , constraint -> bck_field_name_len , dst_node , src_node ) ; if ( err && err != SELVA_EEXIST ) { Selva_NodeId dst_node_id ; int err1 ; fprintf ( stderr , " \n " , __FILE__ , __LINE__ , getSelvaErrorStr ( err ) ) ; SelvaHierarchy_GetNodeId ( dst_node_id , dst_node ) ; err1 = Edge_Delete ( ctx , hierarchy , src_edge_field , src_node , dst_node_id ) ; if ( err1 && err1 != SELVA_ENOENT ) { fprintf ( stderr , " \n " , __FILE__ , __LINE__ , getSelvaErrorStr ( err1 ) ) ; } } else { err = 0 ; } } SelvaSubscriptions_InheritEdge ( ctx , hierarchy , src_node , dst_node , field_name_str , field_name_len ) ; return err ; }
RFE Optimize by taking edgeField as an arg.
RFE Optimize by taking edgeField as an arg .
/*
     * Get src_edge_field
     */ /*
         * single_ref allows only one edge to exist in the field.
         */ /* Just to be sure. */ /*
         * This field is bidirectional and so we need to create an edge pointing back.
         */ /* We must retain the original err. */ /*
             * Ok so, this is a bit dumb but we break an infinite loop by
             * ignoring SELVA_EEXIST. It's terribly inefficient to attempt to
             * create the same edge again just to figure that both ends were
             * created already. This implementation also practically allows
             * multidirectional edges between two nodes.
             * The normal flow should be like this:
             * Edge_Add(src, dst) -> Edge_Add(dst, src) -> Edge_Add(src, dst) => SELVA_EEXIST
             */ /*
             * In case of an error we can't actually rollback anymore but we can
             * delete the edge we just created, perhaps it's still better than
             * leaving a half-broken bidir edge.
             * Surely this can fail too if we are OOMing.
             */ /* We don't want to leak the SELVA_EEXIST. */ /*
     * Note that the regular change events for edges are only sent by the modify
     * command. Therefore we expect we don't need to do anything here.
     */