Why does MAAPI suspend in read() function?

I have the following yang data model, this model should be modified by MAAPI instead of CLI or other north bound interfaces.

container static {
    tailf:hidden sec;
    leaf ip {type string;}
    leaf port {type string;}
}

In my daemon I also subscribe the path ‘/static’, I want that when ip or port is changed, the subscribe will notify me. But when I call the maapi to set it’s value, it is suspend in read() function, look at the following log and call stack.

gdb ./demo
....
TRACE Connected (cdb) to ConfD
TRACE CDB_WAIT_START  --> CONFD_OK
-- trigger point count = 1
-- trigger point val[0] = 6
TRACE CDB_TRIGGER_SUBS TRACE CDB_SUBSCRIPTION_EVENT --> 6
TRACE CDB_NEW_SESSION  --> CONFD_OK
TRACE Established new CDB session to ConfD
TRACE CDB_SET_NAMESPACE  --> CONFD_OK
TRACE CDB_SUB_ITERATE 6
-- VALUE_SET on /static/ip with (oldv) Empty -> (newv) 1457428381
-- VALUE_SET on /static/port with (oldv) Empty -> (newv) 1457428381
TRACE CDB_END_SESSION  --> CONFD_OK
TRACE CDB_SYNC_SUB CDB_DONE_PRIORITY --> CONFD_OK
TRACE MAAPI_START_USER_SESSION  --> CONFD_OK
-- *** Leave trigger thread: 139810245179136, exiting ... 
 --> CONFD_OK
TRACE MAAPI_START_TRANS  --> CONFD_OK
TRACE MAAPI_SET_NAMESPACE  --> CONFD_OK
TRACE MAAPI_SET_ELEM2 /static/ip --> CONFD_OK
TRACE MAAPI_SET_ELEM2 /static/port --> CONFD_OK
TRACE MAAPI_APPLY_TRANS 

Ctrl+C
(gdb) bt
#0  0x00007ffff754125d in read () from /lib64/libpthread.so.0
#1  0x00007ffff69c26a7 in read_fill (fd=20, buf=0x7fffffffd8c0 "\031", len=4) at confd_internal.c:859
#2  0x00007ffff69c6c6a in term_read (socket=socket@entry=20, ret=ret@entry=0x7fffffffd99c, cdbop=cdbop@entry=143) at confd_internal.c:1111
#3  0x00007ffff69c76b4 in op_request_term (sock=sock@entry=20, op=op@entry=143, thandle=thandle@entry=42, isrel=isrel@entry=0, arg=arg@entry=0x6bf370, status=status@entry=0x7fffffffd99c)
    at confd_internal.c:2760
#4  0x00007ffff69ed731 in intcall (sock=sock@entry=20, op=op@entry=143, thandle=thandle@entry=42, req=0x6bf370) at maapi.c:52
#5  0x00007ffff69f33be in maapi_apply_trans_flags (sock=20, tid=42, keepopen=<optimized out>, flags=0) at maapi.c:1550

....

When my daemon is suspend in TRACE MAAPI_APPLY_TRANS , I use gdb to get the call stack, and found it is waiting for reading some data, but when I cancel subscribe the ‘/static’ path, it is ok.

TRACE MAAPI_START_USER_SESSION  --> CONFD_OK
TRACE MAAPI_START_TRANS  --> CONFD_OK
TRACE MAAPI_SET_NAMESPACE  --> CONFD_OK
TRACE MAAPI_SET_ELEM2 /static/ip --> CONFD_OK
TRACE MAAPI_SET_ELEM2 /static/port --> CONFD_OK
TRACE MAAPI_APPLY_TRANS  --> CONFD_OK
TRACE MAAPI_STOP_TRANS  --> CONFD_OK
TRACE MAAPI_END_USER_SESSION  --> CONFD_OK

Here is my maapi code flow:

maapi_start_user_session();
maapi_start_trans();
maapi_set_elem2();
maapi_apply_trans();
maapi_finish_trans();
maapi_end_session();

Hi Lynn,

Note that if you do a MAAPI call like maapi_apply_trans() from a the same thread that also is a CDB subscriber for the data you are committing through MAAPI, you will have a deadlock.

The deadlock occur since ConfD is notifying the subscribers and waiting for a CDB_SYNC_SUB CDB_DONE_PRIORITY, but your application thread cannot serve the CDB subscription event as it is busy waiting for the MAAPI transaction to complete when the subscriber is done. You have a deadlock.

Try performing the MAAPI call from a separate thread, i.e. not in the same context as your subscriber.

Also, it’s not “normal” to modify configuration from within a CDB subscriber. You write “to set it’s value” - what is the “it” and why do you need to set the value? It seems from the trace that you are trying to set values on the same leafs that were just set in the transaction that caused your CDB subscriber to be notified???

OK, I got it thank you!

Hi per, the reason I have to set value to cdb in subscription transaction is as follows:

list interface {
    key "name";
    leaf name {
        type string;
    }
    leaf index {
        tailf:hidden auto-gen;
        type uint32;
    }
}
  1. I have the interface data model like above, the interface’s name is exposed to user while interface’s index is not.

  2. The interface’s index should not configured by user through CLI or netconf, so it’s attribute is hidden.

  3. I receive all the configuration by subscribe the related path, and process all configuration in cdb_diff_iterate().

  4. When new interface is created, the index value should be auto-generated according to interface name, and should be set back to CDB.

  5. Other daemons don’t need to know interface name, they only need the index attribute so they subscribe ‘/interface/index’

list interface {
    tailf:callpoint foocp {
        tailf:transaction-hook subtree;
    }
    key "name";
    leaf name {
        type string;
    }
    leaf index {
        tailf:hidden auto-gen;
        type uint32;
    }
}

Now I use transaction-hook to set the value during each commit, but is seems a little complex, I need to write tailf extension in yang model, write call back, setup maapi and it breaks my config process logic, the interface create config should be process in the hook callback while others in cdb_diff_iterate().

There is another problem, when the daemon restarts, I call cdb_trigger_subscriptions(), so that any configuration could be processed in cdb_diff_iterate(), but the transaction-hook callback is not invoked any more.

This is the correct way (within the ConfD architecture) to handle the case of “additional configuration changes” that need to be done. (Such changes should generally be avoided, but it is OK if - as in your case it seems - the additional configuration is hidden data.) Trying to do this from a CDB subscriber is fraught with problems, the deadlock you encounter is only the first one. It all boils down to the fact that such changes should be done within the same transaction as the “original” changes.

Well, it’s a pretty complex task, at least ConfD provides a way to do it “right”. And tthe things you do in a hook are logically different from those that you do in a CDB subscriber - the former modifies the set of changes done to the configuration, while the latter reacts to the final set of changes by applying them to your system. You can of course keep both parts in the same application / source code file if you wish.

Which is as it should be, and a perfect illustration of how they are different - the work of the hook has already been done and saved in the configuration, there shouldn’t be any need to re-do it on restart of your daemon.