<div dir="ltr"><div>Hi folks,</div><div><br></div>As we&#39;re doing some performance tests in OpenStack using OVN,<div>we noticed that as we keep creating ports, the time for creating a</div><div>single port increases. Also, ovn-northd CPU consumption is quite<div><div>high (see [0] which shows the CPU consumption when creating</div><div>1000 ports and deleting them. Last part where CPU is at 100% is</div><div>when all the ports get deleted).</div><div><br><div><div>With 500 ports in the same Logical Switch, I did some profiling</div><div>of OpenStack neutron-server adding 10 more ports to that Logical</div><div>Switch. Currently, neutron-server spawns different API workers</div><div>(separate processes) which open connections to OVN NB so every</div><div>time an update message is sent from ovsdb-server it&#39;ll be processed</div><div>by all of them.</div><div><br></div><div>In my profiling, I used GreenletProfiler in all those processes to produce</div><div>a trace file and then merged all of them together to aggregate the</div><div>results. In those tests I used OVS master branch compiled it with</div><div>shared libraries to make use of the JSON C parser. Still, I&#39;ve seen</div><div>that most of the time&#39;s been spent in the following two modules:</div><div><br></div><div><font face="monospace, monospace" size="1">- python/ovs/db/data.py:  33%</font></div><div><font face="monospace, monospace" size="1">- uuid.py:  21%</font></div><div><br></div><div>For the data.py module, this is the usage (self time):<br></div><div><br></div><div><font face="monospace, monospace" size="1">Atom.__lt__       16.25%     8283 calls</font></div><div><font face="monospace, monospace" size="1">from_json:118      6.18%   406935 calls</font></div><div><font face="monospace, monospace" size="1">Atom.__hash__      3.48%  1623832 calls</font></div><div><font face="monospace, monospace" size="1">from_json:328      2.01%     5040 calls</font></div><div><font face="monospace, monospace" size="1"><br></font></div><div>While for the uuid module:<br></div><div><div><font face="monospace, monospace" size="1"><br></font></div><div><font face="monospace, monospace" size="1">UUID.__cmp__       12.84%  3570975 calls</font></div></div><div><font face="monospace, monospace" size="1">UUID.__init__       4.06%   362541 calls</font></div><div><font face="monospace, monospace" size="1">UUID.__hash__       2.96%     1800 calls</font></div><div><font face="monospace, monospace" size="1">UUID.__str__        1.03%   355016 calls</font></div><div><font face="monospace, monospace" size="1"><br></font></div><div>Most of the calls to Atom.__lt__ come from BaseOvnIdl.__process_update2(idl.py)</div><div>-&gt; BaseOvnIdl.__row_update(idl.py) -&gt; Datum.__cmp__(data.py) -&gt;</div><div>Atom.__cmp__(data.py).</div><div><br></div><div>The aggregated number of calls to BaseOvnIdl.__process_update2 is</div><div>1400 (and we&#39;re updating only 10 ports!!) while the total connections</div><div>opened to NB database are 10:</div><div><br></div><div><div><font face="monospace, monospace"># netstat -np | grep 6641 | grep python | wc -l</font></div><div><font face="monospace, monospace">10</font></div></div><div><br></div><div>* Bear in mind that those results above were aggregated across all processes.</div><div><br></div><div>Looks like the main culprit for this explosion could be the way we<br></div><div>handle ACLs. As every time we create a port, it&#39;ll belong to a Neutron</div><div>security group (OVN Address Set) and we&#39;ll add a new ACL for every</div><div>Neutron security group rule. If we patch the code to skip the ACL part,</div><div>the time for creating a port remains stable over the time.<br><br></div></div></div></div></div><div>From the comparison tests against ML2/OVS (reference implementation),</div><div>OVN outperforms in most of the operations except for the port creation</div><div>where we can see it can become a bottleneck.</div><div><br></div><div>Before optimizing/redesigning the ACL part, we could do some other</div><div>changes to the way we handle notifications from OVSDB: eg., instead of</div><div>having multiple processes receiving *all* notifications, we could have</div><div>one single process subscribed to those notifications and send a more</div><div>optimized (already parsed) multicast notification to all listening processes</div><div>to keep their own in-memory copies of the DB up to date. All processes</div><div>would connect to NB database in &quot;write-only&quot; mode to commit their</div><div>transactions however.</div><div><br></div><div>Even though this last paragraph would best fit in OpenStack ML I want</div><div>to raise it here for feedback and see if someone can see some &quot;immediate&quot;</div><div>optimization for the way we&#39;re processing notifications from OVSDB.</div><div>Maybe some python binding to do it in C? :)</div><div><br></div><div>Any feedback, comments or suggestions are highly appreciated :)</div><div><br></div><div>Best,</div><div>Daniel Alvarez</div><div><br></div><div>[0] <a href="https://snapshot.raintank.io/dashboard/snapshot/dwbhn0Z1zVTh9kI5j6mCVySx8TvrP45m?orgId=2">https://snapshot.raintank.io/dashboard/snapshot/dwbhn0Z1zVTh9kI5j6mCVySx8TvrP45m?orgId=2</a></div></div>