I run into trouble using the Python Class cdb Subscriber.
internal error (18): confd_internal.c(2740): Unexpected data on socket! 33 -2090335124
Traceback (most recent call last):
File "/home/elcon/confd/confd_bin/src/confd/pyapi/confd/cdb.py", line 253, in _read_sub_socket
points = cdb.read_subscription_socket(self.sock)
_confd.error.Error: internal error (18): confd_internal.c(2740): Unexpected data on socket! 33 -2090335124
When comparing the Python Class with the example cdb_subscription → iter_python -->cdbl.py,
the main difference is the usage of a single socket for subscription and data in the Python class Subscriber.
This might be leading to the issue “Unexpected data on socket”.
Why is such a difference in the confd API and the examples?
Am I right that the PyAPI is official part of the confd software?
The ConfD Python API is an official ConfD API, just as the C and Java API is. Do not use the same socket for subscription and data. Use two separate sockets.
Ok.
But the python class delivered as API, uses only one socket for both. So when I’am using this class, which is the official API, I run into an error. The high level python API (confd) is broken. Only the low level python API (_confd) works as expected.
Here the complete code which is triggering the issue:
import confd
import _confd
import _confd.cdb as cdb
import dhcpd_ns
from confd.cdb import Subscriber
class MyIter(object):
def iterate(kp, op, oldv, newv, state):
return confd.ITER_CONTINUE
def run():
_confd.set_debug(_confd.PROTO_TRACE, sys.stderr)
# Setup subscription
sub2 = Subscriber(name="Sub2", host='127.0.0.1', port=_confd.CONFD_PORT)
point = sub2.register(path='/', iter_obj=MyIter, priority=10)
sub2.start()
sub2.run()
if __name__ == "__main__":
run()
TRACE Connected (cdb) to ConfD
TRACE CDB_SUBSCRIBE
14-Jun-2023::21:11:06.361 227670/7f2f28676000/4 SEND op=32 isrel=0 th=-1 {1,0,0,10,0,[]}
14-Jun-2023::21:11:06.361 227670/7f2f28676000/4 GOT 7
--> CONFD_OK
TRACE CDB_SUBSCRIBE_DONE --> CONFD_OK
14-Jun-2023::21:11:14.240 227670/7f2f28676000/4 GOT {2,1,[7]}
TRACE CDB_SUBSCRIPTION_EVENT --> 7
TRACE CDB_SUB_ITERATE 7
INTERNAL ERROR: confd_internal.c(2740): Unexpected data on socket! 33 -2090335124
internal error (18): confd_internal.c(2740): Unexpected data on socket! 33 -2090335124
Traceback (most recent call last):
File "/home/elcon/confd/confd_bin/src/confd/pyapi/confd/cdb.py", line 253, in _read_sub_socket
points = cdb.read_subscription_socket(self.sock)
_confd.error.Error: internal error (18): confd_internal.c(2740): Unexpected data on socket! 33 -2090335124
I have a new issue when iterating over huge amount of data.
class MyIter(object):
count = 0
def __init__(log):
self.log = log
MyIter.count = 0
def iterate(kp, op, oldv, newv, state):
log.info(f'iterate kp={kp} op={op} oldv={oldv} newv={newv}\t\t{MyIter.count}')
log.info(f'{MyIter.count} {sys.getrefcount(None)}')
MyIter.count += 1
return _confd.ITER_RECURSE
def load_schemas():
with Maapi():
pass
def run():
#_confd.set_debug(_confd.PROTO_TRACE, sys.stderr)
# Setup subscription
sub2 = Subscriber(name="Sub2", host='127.0.0.1', port=_confd.CONFD_PORT)
point = sub2.register(path='/', iter_obj=MyIter, priority=10)
for i in range (1,1000000):
l.append(None)
sub2.start()
if __name__ == "__main__":
run()
It looks like the pyapi lacks a correct reference counting. The py_none ref count is decreasing each iterator loop.
When the ref count is reching 0 an error will be riased:
Fatal Python error: none_dealloc: deallocating None
Python runtime state: initialized
Current thread 0x00007fe4805ff640 (most recent call first):
File "pyapi/confd/cdb.py", line 271 in _read_sub_socket
File "pyapi/confd/cdb.py", line 211 in run
File "/usr/lib/python3.10/threading.py", line 1016 in _bootstrap_inner
File "/usr/lib/python3.10/threading.py", line 973 in _bootstrap
Thread 0x00007fe4829c5000 (most recent call first):
File "/usr/lib/python3.10/threading.py", line 1567 in _shutdown
I’am using the example with the class MyIter, to iterate over all changes. The Yang model doesn’t matter in this case. With netconf edit-config I write a lot of data into the confd database.
The commit is triggering the MyIter callback many times (in my case 300000 times).
After round about 5000 callbacks Python crashes with the error message.
My research showed, that the reference counter to the Python object “None” is gone to 0. This triggeres the deletion of the None object in Python and this isn’t allowed.
To avoid this issue the PyAPI does something like this: Py_INCREF(Py_None); in pyapi/src/_cdb.c::static enum cdb_iter_ret iterator
There is indeed an issue there. The state object (opaque) that can optionally be passed to the iterate method will be None if pre_iterate() is not implemented. But in _cdb.c the reference counter is only increased if the object/opaque is NULL, not Py_None.
If you don’t want to rebuild the ConfD Python API with the above fix, you can just add pre_iterate() and return something other than None as the state object as a workaround. Example:
Thank you.
The workaround work fine.
However recompiling doesn’t.
I tried to build the pyapi with make confd-py3.
But I’ll get:
Traceback (most recent call last):
File "intro/python/1-2-3-start-query-model/dhcpd_conf.py", line 23, in <module>
import confd File "pyapi/confd/__init__.py", line 26, in <module>
import _confd
File "pyapi/_confd/__init__.py", line 12, in <module>
from ._confd_py3 import cdb
ImportError: libconfd.so: cannot open shared object file: No such file or directory
Are you rebuilding the API on macOS arm64?
In any case, if you hit that issue, make, for example, the change below and rebuild the ConfD Python API again: