-
Notifications
You must be signed in to change notification settings - Fork 1
β‘ Bolt: Parallelize rule batch uploads in main.py #73
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
Changes from all commits
da945f5
026db41
c3a161c
33f43b9
1f9ce9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| import unittest | ||
Check warningCode scanning / Pylint (reported by Codacy) Missing module docstring
Missing module docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing module docstring
Missing module docstring
|
||
| from unittest.mock import MagicMock, patch | ||
| import time | ||
| import threading | ||
| from main import push_rules, BATCH_SIZE | ||
| import httpx | ||
|
|
||
| class TestPushRulesPerformance(unittest.TestCase): | ||
Check warningCode scanning / Pylint (reported by Codacy) Missing class docstring
Missing class docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing class docstring
Missing class docstring
|
||
| def setUp(self): | ||
| self.client = MagicMock() | ||
| self.profile_id = "test-profile" | ||
| self.folder_name = "test-folder" | ||
| self.folder_id = "test-folder-id" | ||
| self.do = 1 | ||
Check warningCode scanning / Pylint (reported by Codacy) Attribute name "do" doesn't conform to snake_case naming style
Attribute name "do" doesn't conform to snake_case naming style
Check warningCode scanning / Pylintpython3 (reported by Codacy) Attribute name "do" doesn't conform to snake_case naming style
Attribute name "do" doesn't conform to snake_case naming style
|
||
| self.status = 1 | ||
| self.existing_rules = set() | ||
|
|
||
| @patch('main._api_post_form') | ||
| def test_push_rules_parallel_with_lock(self, mock_post): | ||
| # Create enough hostnames for 5 batches | ||
| num_batches = 5 | ||
| hostnames = [f"host-{i}.com" for i in range(BATCH_SIZE * num_batches)] | ||
|
|
||
| # Mock success | ||
| mock_post.return_value = MagicMock(status_code=200) | ||
|
|
||
| lock = threading.Lock() | ||
|
|
||
| start_time = time.time() | ||
| success = push_rules( | ||
| self.profile_id, | ||
| self.folder_name, | ||
| self.folder_id, | ||
| self.do, | ||
| self.status, | ||
| hostnames, | ||
| self.existing_rules, | ||
| self.client, | ||
| existing_rules_lock=lock | ||
| ) | ||
| duration = time.time() - start_time | ||
|
|
||
| self.assertTrue(success) | ||
| self.assertEqual(mock_post.call_count, num_batches) | ||
| self.assertEqual(len(self.existing_rules), len(hostnames)) | ||
|
|
||
| print(f"\n[Parallel with Lock] Duration: {duration:.4f}s") | ||
|
|
||
| @patch('main._api_post_form') | ||
| def test_push_rules_concurrency(self, mock_post): | ||
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring
Missing function or method docstring
|
||
| # Create enough hostnames for 10 batches | ||
| num_batches = 10 | ||
| hostnames = [f"host-{i}.com" for i in range(BATCH_SIZE * num_batches)] | ||
|
|
||
| # Mock delay to simulate network latency | ||
| def delayed_post(*args, **kwargs): | ||
Check warningCode scanning / Prospector (reported by Codacy) Unused argument 'kwargs' (unused-argument)
Unused argument 'kwargs' (unused-argument)
Check warningCode scanning / Prospector (reported by Codacy) Unused argument 'args' (unused-argument)
Unused argument 'args' (unused-argument)
Check warningCode scanning / Pylint (reported by Codacy) Missing function docstring
Missing function docstring
Check noticeCode scanning / Pylint (reported by Codacy) Unused argument 'kwargs'
Unused argument 'kwargs'
Check noticeCode scanning / Pylint (reported by Codacy) Unused argument 'args'
Unused argument 'args'
Check noticeCode scanning / Pylintpython3 (reported by Codacy) Unused argument 'args'
Unused argument 'args'
Check noticeCode scanning / Pylintpython3 (reported by Codacy) Unused argument 'kwargs'
Unused argument 'kwargs'
|
||
| time.sleep(0.1) | ||
| return MagicMock(status_code=200) | ||
|
|
||
| mock_post.side_effect = delayed_post | ||
|
|
||
| start_time = time.time() | ||
| success = push_rules( | ||
| self.profile_id, | ||
| self.folder_name, | ||
| self.folder_id, | ||
| self.do, | ||
| self.status, | ||
| hostnames, | ||
| self.existing_rules, | ||
| self.client | ||
| ) | ||
| duration = time.time() - start_time | ||
|
|
||
| self.assertTrue(success) | ||
| self.assertEqual(mock_post.call_count, num_batches) | ||
|
|
||
| print(f"\n[Performance Test] Duration for {num_batches} batches with 0.1s latency: {duration:.4f}s") | ||
Check warningCode scanning / Pylint (reported by Codacy) Line too long (108/100)
Line too long (108/100)
Check warningCode scanning / Pylintpython3 (reported by Codacy) Line too long (108/100)
Line too long (108/100)
|
||
|
|
||
| @patch('main._api_post_form') | ||
| def test_push_rules_partial_failure(self, mock_post): | ||
| # Create enough hostnames for 5 batches | ||
| num_batches = 5 | ||
| hostnames = [f"host-{i}.com" for i in range(BATCH_SIZE * num_batches)] | ||
|
|
||
| # Mock failure for some batches | ||
| call_count = 0 | ||
| def partial_failure(*args, **kwargs): | ||
| nonlocal call_count | ||
| call_count += 1 | ||
| # Fail batches 2 and 4 | ||
| if call_count in [2, 4]: | ||
| raise httpx.HTTPError("Simulated API failure") | ||
| return MagicMock(status_code=200) | ||
|
|
||
| mock_post.side_effect = partial_failure | ||
|
|
||
| success = push_rules( | ||
| self.profile_id, | ||
| self.folder_name, | ||
| self.folder_id, | ||
| self.do, | ||
| self.status, | ||
| hostnames, | ||
| self.existing_rules, | ||
| self.client | ||
| ) | ||
|
|
||
| # Should return False when some batches fail | ||
| self.assertFalse(success) | ||
| self.assertEqual(mock_post.call_count, num_batches) | ||
| # Only 3 batches should have succeeded and updated existing_rules | ||
| self.assertEqual(len(self.existing_rules), BATCH_SIZE * 3) | ||
|
|
||
| print(f"\n[Partial Failure Test] {mock_post.call_count} batches attempted, 3 succeeded") | ||
|
|
||
| if __name__ == '__main__': | ||
Check warningCode scanning / Prospector (reported by Codacy) expected 2 blank lines after class or function definition, found 1 (E305)
expected 2 blank lines after class or function definition, found 1 (E305)
|
||
| unittest.main() | ||
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation)