-
Notifications
You must be signed in to change notification settings - Fork 1
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
Bench web/utils #4
base: BenchWeb/frameworks
Are you sure you want to change the base?
Conversation
Reviewer's Guide by SourceryThis pull request adds core infrastructure and benchmarking code for the BenchWeb project, including Docker configurations, database setup, test frameworks, and utilities for running benchmarks. Class diagram for Results and DockerHelper classesclassDiagram
class Results {
-Benchmarker benchmarker
-Config config
-String directory
-String file
-String uuid
-String name
-String environmentDescription
-Dict git
-int startTime
-int completionTime
-List concurrencyLevels
-List pipelineConcurrencyLevels
-List queryIntervals
-List cachedQueryIntervals
-List frameworks
-int duration
-Dict rawData
-Dict completed
-Dict succeeded
-Dict failed
-Dict verify
+parse(tests)
+parse_test(framework_test, test_type)
+parse_all(framework_test)
+write_intermediate(test_name, status_message)
+set_completion_time()
+upload()
+load()
+get_docker_stats_file(test_name, test_type)
+get_raw_file(test_name, test_type)
+get_stats_file(test_name, test_type)
+report_verify_results(framework_test, test_type, result)
+report_benchmark_results(framework_test, test_type, results)
+finish()
}
class DockerHelper {
-Benchmarker benchmarker
-DockerClient client
-DockerClient server
-DockerClient database
+clean()
+build(test, build_log_dir)
+run(test, run_log_dir)
+stop(containers)
+build_databases()
+start_database(database)
+build_wrk()
+test_client_connection(url)
+server_container_exists(container_id_or_name)
+benchmark(script, variables)
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @gitworkflows - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding more robust error handling and validation in shell scripts, particularly around environment variables and command execution
Here's what I looked at during the review
- 🟡 General issues: 2 issues found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 2 issues found
- 🟡 Documentation: 1 issue found
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
problems.append(('fail', "`%s` should be `%s`" % | ||
(''.join(current_neg), | ||
''.join(current_pos)), url)) | ||
except: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Broad exception handling swallows all errors silently
Consider logging the caught exception to aid debugging. Catching all exceptions silently makes it difficult to diagnose issues.
else: | ||
self.timestamp = time.strftime("%Y%m%d%H%M%S", time.localtime()) | ||
|
||
self.run_test_timeout_seconds = 7200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Hardcoded timeout value should be configurable
Consider making the test timeout configurable through command line arguments or configuration file.
self.run_test_timeout_seconds = int(os.getenv('TEST_TIMEOUT_SECONDS', '7200'))
6. Fix this `README.md` and open a pull request | ||
|
||
Starting on line 49 is your actual `README.md` that will sit with your test implementation. Update all the dummy values to their correct values so that when people visit your test in our Github repository, they will be greated with information on how your test implementation works and where to look for useful source code. | ||
|
||
After you have the real `README.md` file in place, delete everything above line 59 and you are ready to open a pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick (documentation): There's a typo: 'greated' should be 'greeted'
with open(self.file, "w") as f: | ||
f.write(json.dumps(self.__to_jsonable(), indent=2)) | ||
|
||
def parse_test(self, framework_test, test_type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider refactoring the parse_test() method to separate parsing logic from file I/O operations.
The parse_test()
method has become difficult to maintain due to deep nesting and mixed responsibilities. Consider extracting the parsing logic into a helper method:
def parse_test(self, framework_test, test_type):
results = {'results': []}
stats = []
raw_file = self.get_raw_file(framework_test.name, test_type)
if os.path.exists(raw_file):
with open(raw_file) as raw_data:
results['results'] = self._parse_raw_data(raw_data)
# Process stats for valid results
for result in results['results']:
if 'startTime' in result and 'endTime' in result:
test_stats = self.__parse_stats(
framework_test, test_type,
result["startTime"], result["endTime"], 1)
stats.append(test_stats)
# Write stats file
with open(self.get_stats_file(framework_test.name, test_type) + ".json", "w") as f:
json.dump(stats, f, indent=2)
return results
def _parse_raw_data(self, raw_data):
"""Parse raw benchmark output data into result dictionaries"""
results = []
is_warmup = True
current_result = None
for line in raw_data:
if "Queries:" in line or "Concurrency:" in line:
is_warmup = False
current_result = None
continue
if "Warmup" in line or "Primer" in line:
is_warmup = True
continue
if not is_warmup:
if current_result is None:
current_result = {}
results.append(current_result)
self._parse_metric_line(line, current_result)
return results
This refactoring:
- Extracts core parsing logic into
_parse_raw_data()
- Reduces nesting depth
- Makes the code more testable and maintainable
- Preserves all existing functionality
The helper method handles the line-by-line parsing while the main method focuses on file I/O and stats processing.
import time | ||
|
||
|
||
class BenchmarkConfig: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider splitting the BenchmarkConfig class into smaller, focused components for network and path configuration.
The configuration class could be simplified by extracting Docker networking and path management into separate components. This would improve maintainability while keeping the core benchmark configuration focused. Here's a suggested refactoring:
class DockerNetworkConfig:
def __init__(self, network_mode, server_host, database_host, client_host):
self.network_mode = network_mode
if network_mode is None:
self.network = 'bw'
self.server_docker_host = "unix://var/run/docker.sock"
self.database_docker_host = "unix://var/run/docker.sock"
self.client_docker_host = "unix://var/run/docker.sock"
else:
self.network = None
self.server_docker_host = f"tcp://{server_host}:2375"
self.database_docker_host = f"tcp://{database_host}:2375"
self.client_docker_host = f"tcp://{client_host}:2375"
class BenchmarkPaths:
def __init__(self, fw_root):
self.fw_root = fw_root
self.db_root = os.path.join(fw_root, "infrastructure", "docker", "databases")
self.lang_root = os.path.join(fw_root, "frameworks")
self.results_root = os.path.join(fw_root, "results")
self.wrk_root = os.path.join(fw_root, "benchmarks", "load-testing", "wrk")
self.scaffold_root = os.path.join(fw_root, "benchmarks", "pre-benchmarks")
class BenchmarkConfig:
def __init__(self, args):
self._init_test_types(args.type)
self._copy_benchmark_args(args)
self.paths = BenchmarkPaths(os.getenv('FWROOT'))
self.network_config = DockerNetworkConfig(
args.network_mode,
args.server_host,
args.database_host,
args.client_host
)
This separates concerns while maintaining functionality:
- DockerNetworkConfig handles network setup logic
- BenchmarkPaths manages path configuration
- BenchmarkConfig focuses on core benchmark settings
The main class becomes cleaner and more maintainable, while related configuration stays logically grouped.
self.completed = dict() | ||
self.succeeded = dict() | ||
self.failed = dict() | ||
self.verify = dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Replace dict()
with {}
(dict-literal
)
self.verify = dict() | |
self.verify = {} |
Explanation
The most concise and Pythonic way to create a dictionary is to use the{}
notation.
This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.
x = {"first": "thing"}
Doing things this way has the added advantage of being a nice little performance
improvement.
Here are the timings before and after the change:
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
Similar reasoning and performance results hold for replacing list()
with []
.
self.failed = dict() | ||
self.verify = dict() | ||
for type in test_types: | ||
self.rawData[type] = dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Replace dict()
with {}
(dict-literal
)
self.rawData[type] = dict() | |
self.rawData[type] = {} |
Explanation
The most concise and Pythonic way to create a dictionary is to use the{}
notation.
This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.
x = {"first": "thing"}
Doing things this way has the added advantage of being a nice little performance
improvement.
Here are the timings before and after the change:
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
Similar reasoning and performance results hold for replacing list()
with []
.
''' | ||
Parses the given test and test_type from the raw_file. | ||
''' | ||
results = dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Replace dict()
with {}
(dict-literal
)
results = dict() | |
results = {} |
Explanation
The most concise and Pythonic way to create a dictionary is to use the{}
notation.
This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.
x = {"first": "thing"}
Doing things this way has the added advantage of being a nice little performance
improvement.
Here are the timings before and after the change:
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
Similar reasoning and performance results hold for replacing list()
with []
.
continue | ||
if not is_warmup: | ||
if rawData is None: | ||
rawData = dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Replace dict()
with {}
(dict-literal
)
rawData = dict() | |
rawData = {} |
Explanation
The most concise and Pythonic way to create a dictionary is to use the{}
notation.
This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.
x = {"first": "thing"}
Doing things this way has the added advantage of being a nice little performance
improvement.
Here are the timings before and after the change:
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
Similar reasoning and performance results hold for replacing list()
with []
.
the parent process' memory from the child process | ||
''' | ||
if framework_test.name not in self.verify.keys(): | ||
self.verify[framework_test.name] = dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Replace dict()
with {}
(dict-literal
)
self.verify[framework_test.name] = dict() | |
self.verify[framework_test.name] = {} |
Explanation
The most concise and Pythonic way to create a dictionary is to use the{}
notation.
This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.
x = {"first": "thing"}
Doing things this way has the added advantage of being a nice little performance
improvement.
Here are the timings before and after the change:
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
Similar reasoning and performance results hold for replacing list()
with []
.
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
PR Type
enhancement, configuration changes, documentation
Description
Results
,Metadata
,DockerHelper
,Scaffolding
,Benchmarker
,FortuneHTMLParser
,FrameworkTest
,TimeLogger
,BenchmarkConfig
,AbstractTestType
,TestType
for various test types,PopenTimeout
,Audit
, and database operations for PostgreSQL and MongoDB.wrk
, including pipeline, concurrency, and query scripts.Changes walkthrough 📝
13 files
wrk.dockerfile
Add Dockerfile for benchmarking with wrk on Ubuntu 24.04
benchmarks/load-testing/wrk/wrk.dockerfile
postgres.dockerfile
Add Dockerfile for PostgreSQL 17 with configurations
infrastructure/docker/databases/postgres/postgres.dockerfile
configurations.
mysql.dockerfile
Add Dockerfile for MySQL 9.0 with configurations
infrastructure/docker/databases/mysql/mysql.dockerfile
mongodb.dockerfile
Add Dockerfile for MongoDB 7.0 with initialization
infrastructure/docker/databases/mongodb/mongodb.dockerfile
.siegerc
Add Configuration File for Siege Load Testing Tool
infrastructure/docker/databases/.siegerc
Dockerfile
Add Dockerfile for BW Docker Image Build
infrastructure/docker/Dockerfile
dependencies.
my.cnf
Add MySQL Configuration File with Performance Settings
infrastructure/docker/databases/mysql/my.cnf
postgresql.conf
Add PostgreSQL Configuration File with Performance Tuning
infrastructure/docker/databases/postgres/postgresql.conf
bw.service
Add Systemd Service File for BW Service Management
infrastructure/docker/services/bw.service
Vagrantfile
Add Vagrantfile for Vagrant Environment Configuration
infrastructure/vagrant/Vagrantfile
benchmark_config.json
Add JSON Configuration Template for Benchmark Tests
benchmarks/pre-benchmarks/benchmark_config.json
60-postgresql-shm.conf
Add Shared Memory Configuration for PostgreSQL
infrastructure/docker/databases/postgres/60-postgresql-shm.conf
60-database-shm.conf
Add Shared Memory Configuration for MySQL
infrastructure/docker/databases/mysql/60-database-shm.conf
39 files
results.py
Implement Results class for benchmark data management
utils/results.py
Results
class for handling benchmark results.verifications.py
Add verification functions for HTTP responses and database updates
benchmarks/verifications.py
metadata.py
Implement Metadata class for test configuration management
utils/metadata.py
Metadata
class for managing test metadata.docker_helper.py
Implement DockerHelper class for Docker container management
utils/docker_helper.py
DockerHelper
class for managing Docker operations.scaffolding.py
Implement Scaffolding class for new test setup
utils/scaffolding.py
Scaffolding
class for setting up new test environments.benchmarker.py
Implement Benchmarker class for orchestrating benchmark tests
benchmarks/benchmarker.py
Benchmarker
class for orchestrating benchmark tests.fortune_html_parser.py
Implement FortuneHTMLParser class for HTML validation
benchmarks/fortune/fortune_html_parser.py
FortuneHTMLParser
class for parsing and validating HTML.framework_test.py
Implement FrameworkTest class for managing tests
benchmarks/framework_test.py
FrameworkTest
class for managing individual tests.time_logger.py
Implement TimeLogger class for execution time tracking
utils/time_logger.py
TimeLogger
class for tracking execution times.benchmark_config.py
Implement BenchmarkConfig class for benchmark settings
utils/benchmark_config.py
BenchmarkConfig
class for configuring benchmarksettings.
abstract_test_type.py
Add Abstract Base Class for Benchmark Test Types
benchmarks/abstract_test_type.py
AbstractTestType
class as an abstract base class for testtypes.
requests.
logic.
fortune.py
Implement Fortune Test Type with HTML Parsing
benchmarks/fortune/fortune.py
TestType
class for the "fortune" test type.FortuneHTMLParser
.abstract_database.py
Define Abstract Base Class for Database Operations
infrastructure/docker/databases/abstract_database.py
AbstractDatabase
class as an abstract base for databaseoperations.
management.
db.py
Implement Database Test Type with JSON Verification
benchmarks/db/db.py
TestType
class for the "db" test type.output_helper.py
Add Logging Utilities with Color and Quiet Mode
utils/output_helper.py
log
function for logging with optional color and prefix.QuietOutputStream
class for conditional output suppression.postgres.py
Implement PostgreSQL Database Operations and Logging
infrastructure/docker/databases/postgres/postgres.py
Database
class for PostgreSQL operations.mongodb.py
Implement MongoDB Database Operations and Logging
infrastructure/docker/databases/mongodb/mongodb.py
Database
class for MongoDB operations.plaintext.py
Implement Plaintext Test Type with Response Verification
benchmarks/plaintext/plaintext.py
TestType
class for the "plaintext" test type.responses.
cached-query.py
Implement Cached Query Test Type with JSON Validation
benchmarks/cached-query/cached-query.py
TestType
class for the "cached-query" test type.query.py
Implement Query Test Type with JSON Validation
benchmarks/query/query.py
TestType
class for the "query" test type.update.py
Implement Update Test Type with JSON Validation
benchmarks/update/update.py
TestType
class for the "update" test type.json.py
Implement JSON Test Type with Response Verification
benchmarks/json/json.py
TestType
class for the "json" test type.popen.py
Add Subprocess Management with Timeout Handling
utils/popen.py
PopenTimeout
class for subprocess management with timeout.__init__.py
Implement Dynamic Database Module Loading and Validation
infrastructure/docker/databases/init.py
audit.py
Add Framework Audit Class for Consistency Checks
utils/audit.py
Audit
class for auditing framework consistency.__init__.py
Implement Dynamic Test Type Module Loading
benchmarks/init.py
create.js
Add MongoDB Initialization Script for Collections
infrastructure/docker/databases/mongodb/create.js
world
andfortune
collections.
pipeline.sh
Add Load Testing Script for Pipeline Using wrk
benchmarks/load-testing/wrk/pipeline.sh
wrk
with pipeline.concurrency.sh
Add Load Testing Script for Concurrency Using wrk
benchmarks/load-testing/wrk/concurrency.sh
wrk
with concurrency.query.sh
Add Load Testing Script for Queries Using wrk
benchmarks/load-testing/wrk/query.sh
wrk
with queryparameters.
bw-startup.sh
Add Startup Script for BW Docker Service Initialization
infrastructure/docker/services/bw-startup.sh
service.
bootstrap.sh
Add Bootstrap Script for Vagrant VM Setup
infrastructure/vagrant/bootstrap.sh
bw-shutdown.sh
Add Shutdown Script for Docker Cleanup and Resource Management
infrastructure/docker/services/bw-shutdown.sh
entry.sh
Add Entry Script for Docker Container Initialization
infrastructure/docker/entry.sh
config.sh
Add PostgreSQL Configuration Script for Settings Update
infrastructure/docker/databases/postgres/config.sh
postgresql.conf
.core.rb
Add Vagrant Provisioning Script for Provider Configuration
infrastructure/vagrant/core.rb
pipeline.lua
Add Lua Script for HTTP Request Pipelining in wrk
benchmarks/load-testing/wrk/pipeline.lua
wrk
to handle HTTP request pipelining.create-postgres.sql
Add SQL Script for PostgreSQL Table Creation and Population
infrastructure/docker/databases/postgres/create-postgres.sql
World
andFortune
.create.sql
Add SQL Script for MySQL Table Creation and Population
infrastructure/docker/databases/mysql/create.sql
world
andfortune
.1 files
custom_motd.sh
Add custom message of the day script for Vagrant
infrastructure/vagrant/custom_motd.sh
2 files
README.md
Add README for Vagrant Development Environment Setup
infrastructure/vagrant/README.md
environment.
README.md
Add README Template for New Benchmark Test Setup
benchmarks/pre-benchmarks/README.md