How to create operational leaf in transaction hook?

Hello,
kindly asking for help from more experienced ConfD users:

I’m using transaction hook in order to suplement user’s transaction to RUNNING with additional data.
I’m using maapi_trans_in_trans in order to supplement configuration data and it works fine.

However when I try to add some optional operational leafs in the list container that user is creating I get errors from confd whatever method I try.

The hook is a transaction-hook of type ‘subtree’ directly on the container of interest.
When I get a ‘.create’ callback on the item in the list container I’m trying to set the operational leaf directly beneath the node being created. The operational leaf is a regular config:false leaf backed up by CDB without a default value.

First method I tried was to open CDB session to operational and it failed on cdb_set_elem.

Second and last method I tried was to execute this code:

int th = maapi_start_trans2(socket, CONFD_OPERATIONAL, CONFD_READ_WRITE, session);
int ret = maapi_cd(socket, th, “/path/to/the/list{new_entry_key}”); //cd is ok
ret = maapi_set_elem2(socket, th, “123”, “op-leaf”); //fails
int err = dal_db_get_error_code(ret); //err == 1 (CONFD_ERR_NOEXISTS)
ret = maapi_apply_trans(socket, th, session);
ret = maapi_finish_trans(socket, th);

The error I get from the maapi_set_elem2 is CONFD_ERR_NOEXISTS(1).

If I change the key (new_entry_key) to the key for an entry that has been there before the user’s transaction then set_elem2 succeeds and I see it changed after user’s transaction is commited (and even before, that is right after this inner operational transaction is commited).

So the question arises: is it possible to set the operational leafs under nodes just being created by user (and do it in transaction hook as opposed to in subscription callback after user’s transaction is commited)? If so how to accomplish that?

Hi,
You can’t mix a transaction of config data to RUNNING with operational data to the OPERATIONAL data store. In your case, the operational data that depend on the new config data should be set through the CDB API or a MAAPI oper “transaction” after the config data has been written to CDB. E.g in the commit phase of the transaction.
Perhaps you can have a CDB config subscriber, with highest priority, write the oper data?

Hello Conny,

by commit phase you mean the phase when the the transaction is commited, changes are applied to DB and the subscriptions are begin sent to notify about the changes as opposed to “pre-commit” phase when hooks and validations take place?

Actually this is how we’ve been doing it so far (for both config and oper data). My task is to move as much of the code as possible from subscriptions to hooks.

If so then the subscriptions (possibly high priority kind) are my only option for operational data, right?

Best regards,
Filip

Well, what you’d really want is for your oper data changes to be part of the same transaction as the required config changes. I.e. not just being able to rely on the list entry actually having been created in the “datastore view” that your hook sees, but also that whatever you do in the hook, it will be reverted if the commit of that transaction fails.

This is a perfectly reasonable “desire”, and it might “just work” - i.e. if you attach to the config transaction as hooks typically do, and write your oper changes in that transaction, they may actually fulfill all of the above - or not. But there is obviously nothing in the documentation that suggests that it would work, and if it doesn’t, you’ll have a hard time convincing anyone that it’s a bug.

Although I no longer actively work with ConfD development (retired), I think this is a rather unfortunate state of affairs. The possibility to write both config and oper data in the same transaction was intentionally added to the ConfD core, but it’s still not “official” functionality that is documented or formally supported.

@per, as each transaction address a specific data store I believe that what’s missing is something like:

int maapi_start_trans_in_trans2(int sock, enum confd_dbname name, enum confd_trans_mode readwrite, int usid, int thandle)

or

int maapi_start_oper_trans_in_trans(int sock, enum confd_trans_mode readwrite, int usid, int thandle)

Although, the NMDA operational-state datastore support for the existing operational datastore will likely take care of this issue. (work-in-progress)

Just write the config and oper data to the operational-state data store from a running data store config change transaction-hook.

I don’t think so, at least not with a name like one of those - the fundamental property of a “trans in trans” is, to quote the documentation for the existing function (emphasis mine) “to start a transaction with another transaction as backend, instead of an actual data store”. In that context, it doesn’t make sense to provide a data store name in addition to the backend transaction handle. And it starts a “real” transaction, which has to be committed/applied to the backend transaction in order to have any effect, while what is wanted here is a single transaction that applies both the config and operational changes, in the standard “all or nothing” fashion.

I guess what you are thinking of is not a way to start another transaction, but to open something like an “extra entry point” to an existing transaction, which allows for writing to a different data store than the one the existing transaction was “addressed” to. But it isn’t really needed from a functional perspective - the data store can be inferred from the config false/true of the nodes being written to. And it actually works already, try using maapi to write some CDB-operational data in a transaction towards running. But as I wrote, it’s not documented and thus not formally supported, and there may well be holes in the existing support which prevents a simple documentation update as the “real fix”.

Back to the question here

@f.barczyk , I finally got around to test this, and it turns out you * can * set operational datastore data from your transaction hook.
From what you wrote above it seems like you only tested with maapi_trans_in_trans() and maapi_start_trans2(), and that’s not going to work here.
You need do a maapi_attach() in for example your init() callback (and a maapi_detatch() in your finish() callback) as @Per mentioned. See examples.confd/intro/11-c_hooks for a reference example.
All you need to do in your example above after attaching is something like:
ret = maapi_set_elem2(socket, th, “123”, “/path/to/the/list{new_entry_key}/op-leaf”);
E.g. from your write_all() or create() callback.

Did you try attaching to the transaction with maapi_attach()?

@cohult , yes :rofl: it works if I don’t start any new transaction nor session just the maapi_attach() that I do anyway for configuration additions.

I don’t know why I didn’t try it the first time… I suppose I had been so many times surprised by confd behavior that it never crossed my mind it would work in the obvious way :wink:

Thank you and @per for help!