Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added in rate limited KytosEventBuffers #412

Closed
wants to merge 13 commits into from
Closed

Conversation

Ktmi
Copy link

@Ktmi Ktmi commented Sep 11, 2023

Closes #245.

Summary

This patch implements a rate limited KytosEventBuffer through the usage of the limits package.

Local Tests

I was trying to test writing many flows to stored_flows, however, at the moment, I get a pymongo error on the flow_manager side of things. Otherwise, normal tasks like creating EVCs seem to work well.

kytos $> data = create_flows('00:00:00:00:00:00:00:01', 50)                                                                                                                                      

kytos $> ev = KytosEvent('kytos.flow_manager.flows.install', content=data)                                                                                                                       

kytos $> controller.buffers.app.put(ev)                                                                                                                                                          

kytos $> 2023-09-11 15:47:31,016 - ERROR [kytos.core.helpers] (thread_pool_app_4) listen_to handler: <function Main.on_flows_install_delete at 0x7fa4583e1900>, args: (<Main(flow_manager, stopped 140342502299200)>, KytosEvent('kytos.flow_manager.flows.install', {'flow_dict': {'flow': [{'priority': 1, 'cookie': 1, 'match': {'in_port': 1, 'dl_vlan': 1}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 2, 'cookie': 2, 'match': {'in_port': 1, 'dl_vlan': 2}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 3, 'cookie': 3, 'match': {'in_port': 1, 'dl_vlan': 3}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 4, 'cookie': 4, 'match': {'in_port': 1, 'dl_vlan': 4}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 5, 'cookie': 5, 'match': {'in_port': 1, 'dl_vlan': 5}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 6, 'cookie': 6, 'match': {'in_port': 1, 'dl_vlan': 6}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 7, 'cookie': 7, 'match': {'in_port': 1, 'dl_vlan': 7}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 8, 'cookie': 8, 'match': {'in_port': 1, 'dl_vlan': 8}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 9, 'cookie': 9, 'match': {'in_port': 1, 'dl_vlan': 9}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 10, 'cookie': 10, 'match': {'in_port': 1, 'dl_vlan': 10}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 11, 'cookie': 11, 'match': {'in_port': 1, 'dl_vlan': 11}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 12, 'cookie': 12, 'match': {'in_port': 1, 'dl_vlan': 12}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 13, 'cookie': 13, 'match': {'in_port': 1, 'dl_vlan': 13}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 14, 'cookie': 14, 'match': {'in_port': 1, 'dl_vlan': 14}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 15, 'cookie': 15, 'match': {'in_port': 1, 'dl_vlan': 15}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 16, 'cookie': 16, 'match': {'in_port': 1, 'dl_vlan': 16}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 17, 'cookie': 17, 'match': {'in_port': 1, 'dl_vlan': 17}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 18, 'cookie': 18, 'match': {'in_port': 1, 'dl_vlan': 18}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 19, 'cookie': 19, 'match': {'in_port': 1, 'dl_vlan': 19}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 20, 'cookie': 20, 'match': {'in_port': 1, 'dl_vlan': 20}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 21, 'cookie': 21, 'match': {'in_port': 1, 'dl_vlan': 21}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 22, 'cookie': 22, 'match': {'in_port': 1, 'dl_vlan': 22}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 23, 'cookie': 23, 'match': {'in_port': 1, 'dl_vlan': 23}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 24, 'cookie': 24, 'match': {'in_port': 1, 'dl_vlan': 24}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 25, 'cookie': 25, 'match': {'in_port': 1, 'dl_vlan': 25}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 26, 'cookie': 26, 'match': {'in_port': 1, 'dl_vlan': 26}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 27, 'cookie': 27, 'match': {'in_port': 1, 'dl_vlan': 27}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 28, 'cookie': 28, 'match': {'in_port': 1, 'dl_vlan': 28}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 29, 'cookie': 29, 'match': {'in_port': 1, 'dl_vlan': 29}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 30, 'cookie': 30, 'match': {'in_port': 1, 'dl_vlan': 30}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 31, 'cookie': 31, 'match': {'in_port': 1, 'dl_vlan': 31}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 32, 'cookie': 32, 'match': {'in_port': 1, 'dl_vlan': 32}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 33, 'cookie': 33, 'match': {'in_port': 1, 'dl_vlan': 33}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 34, 'cookie': 34, 'match': {'in_port': 1, 'dl_vlan': 34}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 35, 'cookie': 35, 'match': {'in_port': 1, 'dl_vlan': 35}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 36, 'cookie': 36, 'match': {'in_port': 1, 'dl_vlan': 36}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 37, 'cookie': 37, 'match': {'in_port': 1, 'dl_vlan': 37}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 38, 'cookie': 38, 'match': {'in_port': 1, 'dl_vlan': 38}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 39, 'cookie': 39, 'match': {'in_port': 1, 'dl_vlan': 39}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 40, 'cookie': 40, 'match': {'in_port': 1, 'dl_vlan': 40}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 41, 'cookie': 41, 'match': {'in_port': 1, 'dl_vlan': 41}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 42, 'cookie': 42, 'match': {'in_port': 1, 'dl_vlan': 42}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 43, 'cookie': 43, 'match': {'in_port': 1, 'dl_vlan': 43}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 44, 'cookie': 44, 'match': {'in_port': 1, 'dl_vlan': 44}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 45, 'cookie': 45, 'match': {'in_port': 1, 'dl_vlan': 45}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 46, 'cookie': 46, 'match': {'in_port': 1, 'dl_vlan': 46}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 47, 'cookie': 47, 'match': {'in_port': 1, 'dl_vlan': 47}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 48, 'cookie': 48, 'match': {'in_port': 1, 'dl_vlan': 48}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 49, 'cookie': 49, 'match': {'in_port': 1, 'dl_vlan': 49}, 'actions': [{'action_type': 'output', 'port': 2}]}, {'priority': 50, 'cookie': 50, 'match': {'in_port': 1, 'dl_vlan': 50}, 'actions': [{'action_type': 'output', 'port': 2}]}]}, 'dpid': '00:00:00:00:00:00:00:01', 'force': True}, 0)) traceback: Traceback (most recent call last):,   File "/home/ktmi/kytos-project3/kytos/kytos/core/helpers.py", line 143, in handler_context,     result = handler(*args),   File "/home/ktmi/kytos-project3/kenv/var/lib/kytos/napps/kytos/flow_manager/main.py", line 538, in on_flows_install_delete,     self.handle_flows_install_delete(event),   File "/home/ktmi/kytos-project3/kenv/var/lib/kytos/napps/kytos/flow_manager/main.py", line 588, in handle_flows_install_delete,     self._install_flows(,   File "/home/ktmi/kytos-project3/kenv/var/lib/kytos/napps/kytos/flow_manager/main.py", line 765, in _install_flows,     self.flow_controller.upsert_flows(,   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/tenacity-8.0.1-py3.10.egg/tenacity/__init__.py", line 324, in wrapped_f,     return self(f, *args, **kw),   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/tenacity-8.0.1-py3.10.egg/tenacity/__init__.py", line 404, in __call__,     do = self.iter(retry_state=retry_state),   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/tenacity-8.0.1-py3.10.egg/tenacity/__init__.py", line 349, in iter,     return fut.result(),   File "/usr/lib/python3.10/concurrent/futures/_base.py", line 451, in result,     return self.__get_result(),   File "/usr/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result,     raise self._exception,   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/tenacity-8.0.1-py3.10.egg/tenacity/__init__.py", line 407, in __call__,     result = fn(*args, **kwargs),   File "/home/ktmi/kytos-project3/kytos/kytos/core/retry.py", line 25, in decorated,     return func(*args, **kwargs),   File "/home/ktmi/kytos-project3/kenv/var/lib/kytos/napps/../napps/kytos/flow_manager/controllers/__init__.py", line 98, in upsert_flows,     return self.db.flows.bulk_write(ops).upserted_ids,   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/pymongo-4.1.0-py3.10-linux-x86_64.egg/pymongo/collection.py", line 514, in bulk_write,     bulk_api_result = blk.execute(write_concern, session),   File "/home/ktmi/kytos-project3/kenv/lib/python3.10/site-packages/pymongo-4.1.0-py3.10-linux-x86_64.egg/pymongo/bulk.py", line 495, in execute,     raise InvalidOperation("No operations to execute"), pymongo.errors.InvalidOperation: No operations to execute, 

End-To-End Tests

The following are the end to end test results:

kytos-end-to-end-tests-kytos-1  | Starting enhanced syslogd: rsyslogd.
kytos-end-to-end-tests-kytos-1  | /etc/openvswitch/conf.db does not exist ... (warning).
kytos-end-to-end-tests-kytos-1  | Creating empty database /etc/openvswitch/conf.db.
kytos-end-to-end-tests-kytos-1  | Starting ovsdb-server.
kytos-end-to-end-tests-kytos-1  | Configuring Open vSwitch system IDs.
kytos-end-to-end-tests-kytos-1  | Starting ovs-vswitchd.
kytos-end-to-end-tests-kytos-1  | Enabling remote OVSDB managers.
kytos-end-to-end-tests-kytos-1  | + sed -i 's/STATS_INTERVAL = 60/STATS_INTERVAL = 7/g' /var/lib/kytos/napps/kytos/of_core/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/CONSISTENCY_MIN_VERDICT_INTERVAL =.*/CONSISTENCY_MIN_VERDICT_INTERVAL = 60/g' /var/lib/kytos/napps/kytos/flow_manager/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/LINK_UP_TIMER = 10/LINK_UP_TIMER = 1/g' /var/lib/kytos/napps/kytos/topology/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/DEPLOY_EVCS_INTERVAL = 60/DEPLOY_EVCS_INTERVAL = 5/g' /var/lib/kytos/napps/kytos/mef_eline/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/LLDP_LOOP_ACTIONS = \["log"\]/LLDP_LOOP_ACTIONS = \["disable","log"\]/' /var/lib/kytos/napps/kytos/of_lldp/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/LLDP_IGNORED_LOOPS = {}/LLDP_IGNORED_LOOPS = {"00:00:00:00:00:00:00:01": \[\[4, 5\]\]}/' /var/lib/kytos/napps/kytos/of_lldp/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/CONSISTENCY_COOKIE_IGNORED_RANGE =.*/CONSISTENCY_COOKIE_IGNORED_RANGE = [(0xdd00000000000000, 0xdd00000000000009)]/g' /var/lib/kytos/napps/kytos/flow_manager/settings.py
kytos-end-to-end-tests-kytos-1  | + sed -i 's/LIVENESS_DEAD_MULTIPLIER =.*/LIVENESS_DEAD_MULTIPLIER = 3/g' /var/lib/kytos/napps/kytos/of_lldp/settings.py
kytos-end-to-end-tests-kytos-1  | + kytosd --help
kytos-end-to-end-tests-kytos-1  | + sed -i s/WARNING/INFO/g /etc/kytos/logging.ini
kytos-end-to-end-tests-kytos-1  | + test -z ''
kytos-end-to-end-tests-kytos-1  | + TESTS=tests/
kytos-end-to-end-tests-kytos-1  | + test -z ''
kytos-end-to-end-tests-kytos-1  | + RERUNS=2
kytos-end-to-end-tests-kytos-1  | + python3 scripts/wait_for_mongo.py
kytos-end-to-end-tests-kytos-1  | Trying to run hello command on MongoDB...
kytos-end-to-end-tests-kytos-1  | Trying to run 'hello' command on MongoDB...
kytos-end-to-end-tests-kytos-1  | Trying to run 'hello' command on MongoDB...
kytos-end-to-end-tests-kytos-1  | Ran 'hello' command on MongoDB successfully. It's ready!
kytos-end-to-end-tests-kytos-1  | + python3 -m pytest tests/ --reruns 2 -r fEr
kytos-end-to-end-tests-kytos-1  | ============================= test session starts ==============================
kytos-end-to-end-tests-kytos-1  | platform linux -- Python 3.9.2, pytest-7.2.0, pluggy-1.2.0
kytos-end-to-end-tests-kytos-1  | rootdir: /tests
kytos-end-to-end-tests-kytos-1  | plugins: rerunfailures-10.2, timeout-2.1.0, anyio-3.6.2
kytos-end-to-end-tests-kytos-1  | collected 241 items
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_01_kytos_startup.py ..                                    [  0%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_05_topology.py ..................                         [  8%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_10_mef_eline.py ..........ss.....x.....x................  [ 24%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_11_mef_eline.py ......                                    [ 27%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_12_mef_eline.py .....Xx.                                  [ 30%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_13_mef_eline.py .....xs.s......xs.s.XXxX.xxxx..X......... [ 47%]
kytos-end-to-end-tests-kytos-1  | ...                                                                      [ 48%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_14_mef_eline.py x                                         [ 49%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_15_mef_eline.py ..                                        [ 50%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_20_flow_manager.py .....................                  [ 58%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_21_flow_manager.py ...                                    [ 60%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_22_flow_manager.py ...............                        [ 66%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_23_flow_manager.py ..............                         [ 72%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_30_of_lldp.py ....                                        [ 73%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_31_of_lldp.py ...                                         [ 75%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_32_of_lldp.py ...                                         [ 76%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_40_sdntrace.py ............RRF                            [ 81%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_41_kytos_auth.py ........                                 [ 85%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_50_maintenance.py ........................                [ 95%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_60_of_multi_table.py .....                                [ 97%]
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_70_kytos_stats.py .......                                 [100%]
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | =================================== FAILURES ===================================
kytos-end-to-end-tests-kytos-1  | _____________ TestE2ESDNTrace.test_090_test_flows_with_instruction _____________
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | cls = <tests.test_e2e_40_sdntrace.TestE2ESDNTrace object at 0x7fdaae448160>
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  |     def test_090_test_flows_with_instruction(cls):
kytos-end-to-end-tests-kytos-1  |         "Test flows with instruction"
kytos-end-to-end-tests-kytos-1  |         payload_stored_flow = {
kytos-end-to-end-tests-kytos-1  |             "flows": [
kytos-end-to-end-tests-kytos-1  |                 {
kytos-end-to-end-tests-kytos-1  |                     "match": {
kytos-end-to-end-tests-kytos-1  |                         "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                         "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                     },
kytos-end-to-end-tests-kytos-1  |                     "instructions": [
kytos-end-to-end-tests-kytos-1  |                         {
kytos-end-to-end-tests-kytos-1  |                             "instruction_type": "apply_actions",
kytos-end-to-end-tests-kytos-1  |                             "actions": [
kytos-end-to-end-tests-kytos-1  |                                 {"action_type": "output", "port": 1}
kytos-end-to-end-tests-kytos-1  |                             ]
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     ]
kytos-end-to-end-tests-kytos-1  |                 }
kytos-end-to-end-tests-kytos-1  |             ]
kytos-end-to-end-tests-kytos-1  |         }
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/kytos/flow_manager/v2/flows/00:00:00:00:00:00:00:01'
kytos-end-to-end-tests-kytos-1  |         response = requests.post(api_url, json = payload_stored_flow)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 202, response.text
kytos-end-to-end-tests-kytos-1  |         time.sleep(10)
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         payload = [
kytos-end-to-end-tests-kytos-1  |                     {
kytos-end-to-end-tests-kytos-1  |                         "trace": {
kytos-end-to-end-tests-kytos-1  |                             "switch": {
kytos-end-to-end-tests-kytos-1  |                                 "dpid": "00:00:00:00:00:00:00:01",
kytos-end-to-end-tests-kytos-1  |                                 "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                             },
kytos-end-to-end-tests-kytos-1  |                             "eth": {
kytos-end-to-end-tests-kytos-1  |                                 "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                             }
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     }
kytos-end-to-end-tests-kytos-1  |                 ]
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/amlight/sdntrace_cp/v1/traces'
kytos-end-to-end-tests-kytos-1  |         response = requests.put(api_url, json=payload)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 200, response.text
kytos-end-to-end-tests-kytos-1  | >       data = response.json()["result"][0][0]
kytos-end-to-end-tests-kytos-1  | E       IndexError: list index out of range
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_40_sdntrace.py:1037: IndexError
kytos-end-to-end-tests-kytos-1  | ---------------------------- Captured stdout setup -----------------------------
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | ---------------------------- Captured stdout setup -----------------------------
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | ---------------------------- Captured stdout setup -----------------------------
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | FAIL to stop kytos after 5 seconds -- Kytos pid still exists.. Force stop!
kytos-end-to-end-tests-kytos-1  | =============================== warnings summary ===============================
kytos-end-to-end-tests-kytos-1  | usr/local/lib/python3.9/dist-packages/kytos/core/config.py:187
kytos-end-to-end-tests-kytos-1  |   /usr/local/lib/python3.9/dist-packages/kytos/core/config.py:187: UserWarning: Unknown arguments: ['tests/', '--reruns', '2', '-r', 'fEr']
kytos-end-to-end-tests-kytos-1  |     warnings.warn(f"Unknown arguments: {unknown}")
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | test_e2e_01_kytos_startup.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_05_topology.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_10_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_11_mef_eline.py: 25 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_12_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_13_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_14_mef_eline.py: 76 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_15_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_20_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_21_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_22_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_23_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_30_of_lldp.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_31_of_lldp.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_32_of_lldp.py: 11 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_40_sdntrace.py: 147 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_41_kytos_auth.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_50_maintenance.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_60_of_multi_table.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_70_kytos_stats.py: 17 warnings
kytos-end-to-end-tests-kytos-1  |   /usr/lib/python3/dist-packages/mininet/node.py:1121: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
kytos-end-to-end-tests-kytos-1  |     return ( StrictVersion( cls.OVSVersion ) <
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | test_e2e_01_kytos_startup.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_05_topology.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_10_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_11_mef_eline.py: 25 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_12_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_13_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_14_mef_eline.py: 76 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_15_mef_eline.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_20_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_21_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_22_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_23_flow_manager.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_30_of_lldp.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_31_of_lldp.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_32_of_lldp.py: 11 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_40_sdntrace.py: 147 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_41_kytos_auth.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_50_maintenance.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_60_of_multi_table.py: 17 warnings
kytos-end-to-end-tests-kytos-1  | test_e2e_70_kytos_stats.py: 17 warnings
kytos-end-to-end-tests-kytos-1  |   /usr/lib/python3/dist-packages/mininet/node.py:1122: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
kytos-end-to-end-tests-kytos-1  |     StrictVersion( '1.10' ) )
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
kytos-end-to-end-tests-kytos-1  | ------------------------------- start/stop times -------------------------------
kytos-end-to-end-tests-kytos-1  | rerun: 0
kytos-end-to-end-tests-kytos-1  | test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction: 2023-09-08,23:02:28.393561 - 2023-09-08,23:02:38.438356
kytos-end-to-end-tests-kytos-1  | cls = <tests.test_e2e_40_sdntrace.TestE2ESDNTrace object at 0x7fdaae448160>
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  |     def test_090_test_flows_with_instruction(cls):
kytos-end-to-end-tests-kytos-1  |         "Test flows with instruction"
kytos-end-to-end-tests-kytos-1  |         payload_stored_flow = {
kytos-end-to-end-tests-kytos-1  |             "flows": [
kytos-end-to-end-tests-kytos-1  |                 {
kytos-end-to-end-tests-kytos-1  |                     "match": {
kytos-end-to-end-tests-kytos-1  |                         "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                         "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                     },
kytos-end-to-end-tests-kytos-1  |                     "instructions": [
kytos-end-to-end-tests-kytos-1  |                         {
kytos-end-to-end-tests-kytos-1  |                             "instruction_type": "apply_actions",
kytos-end-to-end-tests-kytos-1  |                             "actions": [
kytos-end-to-end-tests-kytos-1  |                                 {"action_type": "output", "port": 1}
kytos-end-to-end-tests-kytos-1  |                             ]
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     ]
kytos-end-to-end-tests-kytos-1  |                 }
kytos-end-to-end-tests-kytos-1  |             ]
kytos-end-to-end-tests-kytos-1  |         }
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/kytos/flow_manager/v2/flows/00:00:00:00:00:00:00:01'
kytos-end-to-end-tests-kytos-1  |         response = requests.post(api_url, json = payload_stored_flow)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 202, response.text
kytos-end-to-end-tests-kytos-1  |         time.sleep(10)
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         payload = [
kytos-end-to-end-tests-kytos-1  |                     {
kytos-end-to-end-tests-kytos-1  |                         "trace": {
kytos-end-to-end-tests-kytos-1  |                             "switch": {
kytos-end-to-end-tests-kytos-1  |                                 "dpid": "00:00:00:00:00:00:00:01",
kytos-end-to-end-tests-kytos-1  |                                 "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                             },
kytos-end-to-end-tests-kytos-1  |                             "eth": {
kytos-end-to-end-tests-kytos-1  |                                 "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                             }
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     }
kytos-end-to-end-tests-kytos-1  |                 ]
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/amlight/sdntrace_cp/v1/traces'
kytos-end-to-end-tests-kytos-1  |         response = requests.put(api_url, json=payload)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 200, response.text
kytos-end-to-end-tests-kytos-1  | >       data = response.json()["result"][0][0]
kytos-end-to-end-tests-kytos-1  | E       IndexError: list index out of range
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_40_sdntrace.py:1037: IndexError
kytos-end-to-end-tests-kytos-1  | rerun: 1
kytos-end-to-end-tests-kytos-1  | test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction: 2023-09-08,23:03:43.125244 - 2023-09-08,23:03:53.172277
kytos-end-to-end-tests-kytos-1  | cls = <tests.test_e2e_40_sdntrace.TestE2ESDNTrace object at 0x7fdaae448160>
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  |     def test_090_test_flows_with_instruction(cls):
kytos-end-to-end-tests-kytos-1  |         "Test flows with instruction"
kytos-end-to-end-tests-kytos-1  |         payload_stored_flow = {
kytos-end-to-end-tests-kytos-1  |             "flows": [
kytos-end-to-end-tests-kytos-1  |                 {
kytos-end-to-end-tests-kytos-1  |                     "match": {
kytos-end-to-end-tests-kytos-1  |                         "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                         "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                     },
kytos-end-to-end-tests-kytos-1  |                     "instructions": [
kytos-end-to-end-tests-kytos-1  |                         {
kytos-end-to-end-tests-kytos-1  |                             "instruction_type": "apply_actions",
kytos-end-to-end-tests-kytos-1  |                             "actions": [
kytos-end-to-end-tests-kytos-1  |                                 {"action_type": "output", "port": 1}
kytos-end-to-end-tests-kytos-1  |                             ]
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     ]
kytos-end-to-end-tests-kytos-1  |                 }
kytos-end-to-end-tests-kytos-1  |             ]
kytos-end-to-end-tests-kytos-1  |         }
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/kytos/flow_manager/v2/flows/00:00:00:00:00:00:00:01'
kytos-end-to-end-tests-kytos-1  |         response = requests.post(api_url, json = payload_stored_flow)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 202, response.text
kytos-end-to-end-tests-kytos-1  |         time.sleep(10)
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         payload = [
kytos-end-to-end-tests-kytos-1  |                     {
kytos-end-to-end-tests-kytos-1  |                         "trace": {
kytos-end-to-end-tests-kytos-1  |                             "switch": {
kytos-end-to-end-tests-kytos-1  |                                 "dpid": "00:00:00:00:00:00:00:01",
kytos-end-to-end-tests-kytos-1  |                                 "in_port": 2,
kytos-end-to-end-tests-kytos-1  |                             },
kytos-end-to-end-tests-kytos-1  |                             "eth": {
kytos-end-to-end-tests-kytos-1  |                                 "dl_vlan": 100
kytos-end-to-end-tests-kytos-1  |                             }
kytos-end-to-end-tests-kytos-1  |                         }
kytos-end-to-end-tests-kytos-1  |                     }
kytos-end-to-end-tests-kytos-1  |                 ]
kytos-end-to-end-tests-kytos-1  |     
kytos-end-to-end-tests-kytos-1  |         api_url = KYTOS_API + '/amlight/sdntrace_cp/v1/traces'
kytos-end-to-end-tests-kytos-1  |         response = requests.put(api_url, json=payload)
kytos-end-to-end-tests-kytos-1  |         assert response.status_code == 200, response.text
kytos-end-to-end-tests-kytos-1  | >       data = response.json()["result"][0][0]
kytos-end-to-end-tests-kytos-1  | E       IndexError: list index out of range
kytos-end-to-end-tests-kytos-1  | 
kytos-end-to-end-tests-kytos-1  | tests/test_e2e_40_sdntrace.py:1037: IndexError
kytos-end-to-end-tests-kytos-1  | test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction: 2023-09-08,23:04:59.179691 - 2023-09-08,23:05:09.224641
kytos-end-to-end-tests-kytos-1  | =========================== rerun test summary info ============================
kytos-end-to-end-tests-kytos-1  | RERUN test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction
kytos-end-to-end-tests-kytos-1  | RERUN test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction
kytos-end-to-end-tests-kytos-1  | =========================== short test summary info ============================
kytos-end-to-end-tests-kytos-1  | FAILED tests/test_e2e_40_sdntrace.py::TestE2ESDNTrace::test_090_test_flows_with_instruction
kytos-end-to-end-tests-kytos-1  | = 1 failed, 218 passed, 6 skipped, 11 xfailed, 5 xpassed, 1063 warnings, 2 rerun in 11147.00s (3:05:47) =

@Ktmi Ktmi requested a review from a team as a code owner September 11, 2023 15:54
Copy link
Member

@viniarck viniarck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent contribution, @Ktmi. Once again, I appreciated your help with this. Otherwise, we wouldn't have this on this 2023.2 version.

I look forward to also seeing the FlowMods stress test, if you could also plot a graph, using wireshark with the plot IO once if filter for FlowMod OpenFlow msg type that can be an option. Also, regarding the e2e test failures, they look unrelated, but if you hit it again please file an issue and ping @gretelliz to take a look (on nightly tests it's passing though), she's been enhancing sdntrace_cp test suite too.

Also, regarding generating flows, looks like you've hit kytos-ng/flow_manager#104, check out the payload again.

requirements/run.txt Outdated Show resolved Hide resolved
kytos/core/buffers.py Outdated Show resolved Hide resolved
kytos/core/buffers.py Outdated Show resolved Hide resolved
@Ktmi
Copy link
Author

Ktmi commented Sep 20, 2023

Still getting the same error in the SDN trace e2e tests. Not sure why. Will rerun without this patch and see if the issue persists.

@Ktmi
Copy link
Author

Ktmi commented Sep 20, 2023

E2E tests on SDN trace fails whether I have this patch applied or not.

@viniarck
Copy link
Member

E2E tests on SDN trace fails whether I have this patch applied or not.

@Ktmi the nightly e2e tests have been passing for more than 2 weeks. Maybe it might be a sleep that's impacting when executing on your laptop? Either way, if it's failing it might be worth mapping an issue for it if you haven't yet, just so it can be improved. It's also worth dispatching an execution on GitLab https://gitlab.ampath.net/amlight/kytos-end-to-end-tester/-/pipelines with this branch here just so you can also have more information when opening the issue.

@viniarck
Copy link
Member

E2E tests on SDN trace fails whether I have this patch applied or not.

@Ktmi the nightly e2e tests have been passing for more than 2 weeks. Maybe it might be a sleep that's impacting when executing on your laptop? Either way, if it's failing it might be worth mapping an issue for it if you haven't yet, just so it can be improved. It's also worth dispatching an execution on GitLab https://gitlab.ampath.net/amlight/kytos-end-to-end-tester/-/pipelines with this branch here just so you can also have more information when opening the issue.

If you set this env var ENV_VARS with this value --build-arg branch_kytos=feat/rate_limited_buffers and dispatch the GitLab pipeline it should run all the e2e tests with this branch.

@viniarck viniarck self-requested a review September 20, 2023 16:34
Copy link
Member

@viniarck viniarck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ktmi, consider this PR approved. A few points though (other than the e2e test we're discussing on other comments):

  1. Please, simulate a queue misconfiguration and see what will happen. Ideally, it should exit kytosd if it can't handle the option, same pattern that we use with other unsupported option values.
  2. Also, let us know if you had the chance to stress test the rate limit in practice and see it working.
  3. Also I raised a question regarding the current supported configuration and if we need more flexibility or not.

Thanks

kytos/core/buffers/factory.py Outdated Show resolved Hide resolved
kytos/templates/kytos.conf.template Outdated Show resolved Hide resolved
@viniarck viniarck self-requested a review September 21, 2023 16:33
Copy link
Member

@viniarck viniarck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it pre-approved. @Ktmi once you stress test the rate limit in practice and confirm it's working feel free to merge.

Regarding the future discussion, let's leave that for another issue, and maybe we might not even need to prioritize we'll see, and it's OK to only address that in the future and break compat once we have other clear usages, sometimes it's also better to wait for the concrete use cases, we still have reasonable time frame until 2023.2 is shipped, so if any other case is needed we'll reprioritize otherwise we'll ship the current one, OK?

@viniarck viniarck self-requested a review October 4, 2023 13:17
Copy link
Member

@viniarck viniarck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks good, but still waiting for the following (so I'll review again to make it clear what's left to merge this PR) just so this PR can land:

  • Exercise the rate limits in practice and confirm the expected rate.
  • Resolve the changelog conflict.
  • Confirm that e2e tests are still passing and map whatever else if needed, if any.

Thanks, David.

@viniarck viniarck self-requested a review October 17, 2023 18:03

import limits
import limits.storage as lstorage
import limits.strategies as lstrategies
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ktmi, I was looking into limits source code and docs, and there's exclusive async/aio strategies, check it out, also the storage has a corresponding async abstraction that's asyncio.Task based instead of using treading.Timer, so let's make sure we're also using asyncio all the way down where possible. As a result, later on when calling hit it'd be a bool coroutine await moving_window.hit(one_per_minute, "test_namespace", "foo").

Locally, I was exploring a 20/second limit, but then I realized that it was not reaching 20/seconds for a given dpid check out the graph below, in red it's filtering for one dpid, and in blue it's all pkt out messages in a ring topology that I was using, I also included some logs to see how it was sleeping, and even though in most cases it was only sleeping after fetching roughly 20 items or so (or hitting them 20), but in some cases it was only getting 10 more or less, looks like it's sleeping more than it should, so I'm going to recommend for you to research and double check this part again, and check if we can potentially use the remaing value from WindowStats when using the get_window_stats, if it actually returns the remaining time when it might also simplifies the current subtraction that's being computed.

2023-10-17 16:10:19,006 - INFO [kytos.core.buffers.buffers] [buffers.py:162:aget] (MainThread) buffer msg_out will sleep for: 0.9936294555664062 secs, q len: 905, ids: (20 per 1 second,
 ('127.0.0.1', 57754))
2023-10-17 16:10:20,017 - INFO [kytos.core.buffers.buffers] [buffers.py:162:aget] (MainThread) buffer msg_out will sleep for: 0.9823873043060303 secs, q len: 877, ids: (20 per 1 second,
 ('127.0.0.1', 57754))
2023-10-17 16:10:21,011 - INFO [kytos.core.buffers.buffers] [buffers.py:162:aget] (MainThread) buffer msg_out will sleep for: 0.9889931678771973 secs, q len: 868, ids: (20 per 1 second,
 ('127.0.0.1', 57754))
2023-10-17 16:10:22,018 - INFO [kytos.core.buffers.buffers] [buffers.py:162:aget] (MainThread) buffer msg_out will sleep for: 0.9818587303161621 secs, q len: 856, ids: (20 per 1 second,
 ('127.0.0.1', 57754))
2023-10-17 16:10:23,018 - INFO [kytos.core.buffers.buffers] [buffers.py:162:aget] (MainThread) buffer msg_out will sleep for: 0.9812984466552734 secs, q len: 836, ids: (20 per 1 second,
 ('127.0.0.1', 57754))

20231017_161154

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've seen the async version, but the reason I didn't use it because, then I would have to have two separate rate limiters between the async and non async version. However, it seems that we don't use non-async get, so I can change this to the pure async version if we go under the assumption that get is deprecated and that we only use aget.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Let's go with pure async then and leave the sync part unsupported. All queue consumers on core are async, they're either using msg_out_event_handler or the generic event_handler.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched the code to async. Not sure what's causing the rate to be halved like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ktmi, I'm suspecting it might be the sleep interval, in some iterations if it sleeps more than expected then it makes sense that the rate halved. Maybe the remaing value from WindowStats when using the get_window_stats might be helpful if you could look into it and then later on confirm with a local stress test too. I generated this graph with wireshark > IO graph sending a bunch of FlowMods. If you need help let me know.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing the same behaviour you are. For the following graph, I multiplied the packets sent by of_lldp so it would send 192 to a single switch every 3 seconds with a limit of 100/second, and here are the results.
image

Same conditions as above, but I have of_lldp try to send 400 packets every 3 seconds.

image

In both above graphs, their are periods where packets aren't being sent at the same throughput, but the max throughput isn't being limited in the same way you are showing.

As for the remaining value from window_stats, that's the amount of room left in the window, I'll add a guard to try again if the value is not 0.

Copy link
Member

@viniarck viniarck Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ktmi,

their are periods where packets aren't being sent at the same throughput, but the max throughput isn't being limited in the same way you are showing.

That's certainly a positive point knowing the max throughput didn't get limited, but with a rate of every 3 secs it might not be hitting the issue that I'm seeing. To facilitate to reproduce the issue, I've tried to reduce the minimal scenario possible, I've manage to reproduce using this stress test tool called vegeta, by sending requests to flow manager. Arguably the minimal code path would be without sending requests and the DB, but these aren't bottlenecks for the rate I'm using so it's OK.

Here are my findings and the stress test command. For all stress tests I'm always sending 100 request per second with a single flow over 30 seconds to sustain the throughput to get close to 100 packets per second:

❯ jq -ncM '{method: "POST", url:"http://localhost:8181/api/kytos/flow_manager/v2/flows/00:00:00:00:00:00:00:01", body: { "force": false, "flows": [ { "priority": 10, "match": { "in_port": 1, "dl_vlan": 100 }, "actions": [ { "action_type": "output", "port": 1 } ] } ] } | @base64, header: {"Content-Type": ["application/json"]}}' | vegeta attack -format=json -rate 100/1s -duration=30s -timeout=60s | tee results.bin | vegeta report
  • Case 0 -> To prove that 100 pps (FlowMods ps) can be reached and the DB isn't a bottleneck for this rate:

20231026_151841

  • Case 1 -> I've set msg_out limit as 20/second, notice that rate of flow mods aren't relatively constant and it's almost as if it were halved:

20231026_152948

  • Rerunning case 1 again -> Sometimes it even goes over 10 pps, so it's not like a hard half rate limit, but on average it looks like it is, it's actually a bit lower since the rate actually sent on wire isn't constant:

20231026_161021

  • Case 2 -> I've set msg_out limit as 100/second (default one), again, it behaved the same as the prior example except resulting in 50/second:

20231026_153246

  • Case 3 -> I've set msg_out limit as 300/second, it didn't get rate limited as expected and FlowMods were sent at a relatively constant rate of 100/sec matching the client rate, so proving that without sleeping for a constant rate it's not interfering either as expected:

20231026_153549

Conclusion, I still suspect it might be the value it's sleeping for and/or maybe also try out other strategies type such as fixed window, I haven't further debugged this specfically this time, but it's behaving as I saw the other day. Let me know if you can try to reproduce on your env with vegeta, otherwise we can try to pair on it. Thanks, David.

@viniarck
Copy link
Member

Other than that I also simulated a wrong configuration and it exited kytosd as expected:

Web update - Web UI was not updated
2023-10-17 15:18:27,786 - INFO [kytos.core.db] [db.py:152:db_conn_wait] (MainThread) Starting DB connection
2023-10-17 15:18:27,787 - INFO [kytos.core.db] [db.py:137:_mongo_conn_wait] (MainThread) Trying to run 'hello' command on MongoDB...
2023-10-17 15:18:27,795 - INFO [kytos.core.db] [db.py:139:_mongo_conn_wait] (MainThread) Ran 'hello' command on MongoDB successfully. It's ready!
2023-10-17 15:18:27,805 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/login/ - GET
2023-10-17 15:18:27,806 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/users/ - GET
2023-10-17 15:18:27,806 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/users/{username} - GET
2023-10-17 15:18:27,806 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/users/ - POST
Kytos couldn't start because of 'rate_limitedx' Traceback (most recent call last):
  File "/home/viniarck/repos/kytos/kytos/core/controller.py", line 264, in start
    self.start_controller()
  File "/home/viniarck/repos/kytos/kytos/core/controller.py", line 342, in start_controller
    self._buffers = KytosBuffers()
  File "/home/viniarck/repos/kytos/kytos/core/buffers/manager.py", line 60, in __init__
    setattr(self, name, buffer_from_config(name, config))
  File "/home/viniarck/repos/kytos/kytos/core/buffers/factory.py", line 89, in buffer_from_config
    processed_conf = buffer_conf_processors[buffer_type](config)
KeyError: 'rate_limitedx'

Task was destroyed but it is pending!
task: <Task pending name='Task-1' coro=<start_shell_async() running at /home/viniarck/repos/kytos/kytos/core/kytosd.py:120>>
2023-10-17 15:18:27,806 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/users/{username} - DELETE
2023-10-17 15:18:27,806 - INFO [kytos.core.api_server] [api_server.py:429:_start_endpoint] (MainThread) Started /api/kytos/core/auth/users/{username} - PATCH
2023-10-17 15:18:27,806 - INFO [kytos.core.controller] [controller.py:282:create_pidfile] (MainThread) /home/viniarck/repos/kytos/.direnv/python-3.9/var/run/kytos
2023-10-17 15:18:27,807 - INFO [kytos.core.controller] [controller.py:340:start_controller] (MainThread) Starting Kytos - Kytos Controller
2023-10-17 15:18:27,810 - ERROR [kytos.core.controller] [kytosd.py:156:async_main] (MainThread) Kytos couldn't start because of 'rate_limitedx' Traceback (most recent call last):
  File "/home/viniarck/repos/kytos/kytos/core/controller.py", line 264, in start
    self.start_controller()
  File "/home/viniarck/repos/kytos/kytos/core/controller.py", line 342, in start_controller
    self._buffers = KytosBuffers()
  File "/home/viniarck/repos/kytos/kytos/core/buffers/manager.py", line 60, in __init__
    setattr(self, name, buffer_from_config(name, config))
  File "/home/viniarck/repos/kytos/kytos/core/buffers/factory.py", line 89, in buffer_from_config
    processed_conf = buffer_conf_processors[buffer_type](config)
KeyError: 'rate_limitedx'

/home/viniarck/repos/kytos/kytos/core/kytosd.py:98: RuntimeWarning: coroutine 'start_shell_async' was never awaited
  async_main(config)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2023-10-17 15:18:27,810 - INFO [kytos.core.controller] [kytosd.py:157:async_main] (MainThread) Shutting down Kytos...

event = KytosEvent(name='kytos/core.shutdown')
for buffer in self.get_all_buffers():
buffer.put(event)
async def aget(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

David, another requirement to consider:

  • Also, rate limit at the put and aput level so before actually putting in the queue, essentially rate limiting as close to the source as possible as far as the queue is concerned. This also implies supporting both sync and async for this end of the queue.

Let me elaborate on the use case in the next comments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is partly related a prior chaining discussion, but here it would only chain on the put|aput where any publisher could register a particular rate limiter that it'll use. Or alternatively provide a context manager or something similar where publisher who need to be rate limiter before putting in the queue they can use that context or async context, although I believe the former would be less boilerplate and more ergonomic to use.

Considering that we've been using bounded queues, this will also contribute to a bit more of fairness with other concurrent producers who might be sending other type of messages regardless of using a priority queue or not (and even more when it's using a priority queue), when rate limiters are also being used on the put|aput side. It's a subtle difference, but if the queue is properly sized for the current number of events going on, then it can make a significant difference since the queue won't be constantly getting full.

                                                          producer 1 (put)
core event_handler aget <---  Queue X   <----  put|aput   producer 2 (aput)
                                                          ....
                                                          producer n (put|aput)
    |
    |
    \/
________________________________

NApps event handlers run by

thread pool and/or asyncio task

________________________________


aget working as a global rate limiter with a given identifier as it's currently implemented, and then also structure on put|aput in a way where in the future it's also possible to throttle with a given unique id context, allowing it to essentially replacing implementing rate limiting at the producer level as we've been doing for mef_eline and telemetry_int (even though the global aget rate limit will already solve a major part of the problem).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concrete use case example with our existing Kytos-ng NApps:

  • globally rate limit any messages per 'destination.id' on msg_out at 100/second AND individually rate limit any flows from mef_eline at '30/sec' and any from telemetry_int at '30/sec'.

On msg_out, flow_manager is responsible for pushing FlowMods, so it's centralizing the events, so on flow_manager it could on behalf of its clients rate limit based on the flow owner value:

                                                     mef_eline (via requests and KytosEvent)
aget  <---  Queue msg_out <---  put   flow_manager

                                                    telemetry_int (via KytosEvent)

Do you see what I mean?

Can you also support this or propose another approach to also solve the publisher side rate limit?

@Ktmi
Copy link
Author

Ktmi commented Jan 29, 2024

Gonna close this one, and reopen a new PR, in order to integrate the changes from #438.

@Ktmi Ktmi closed this Jan 29, 2024
@Ktmi Ktmi mentioned this pull request Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for rate limiting on core queues
2 participants