Skip to content

Commit 9dec526

Browse files
committed
final code changes to comply initial task description better
1 parent 96b4f54 commit 9dec526

File tree

6 files changed

+74
-32
lines changed

6 files changed

+74
-32
lines changed

BackgroundWorker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <condition_variable>
34
#include <mutex>
45
#include <string>
56

CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
cmake_minimum_required(VERSION 3.15) # 3.2 for Hunter; 3.15 for MSVC_RUNTIME_LIBRARY
2+
set(CMAKE_POLICY_DEFAULT_CMP0126 OLD) # for Hunter # New in version 3.21
3+
14
# ====================================
25

36
set(CMAKE_MESSAGE_LOG_LEVEL "VERBOSE" CACHE STRING "Log verbosity (STATUS | VERBOSE | DEBUG | TRACE)")
@@ -9,9 +12,6 @@ set(DEPS_REQUIRED_RAPIDJSON_VERSION "1.1.0-b557259-p0" CACHE STRING "Required pa
912
# Setup Hunter package manager
1013
# ====================================
1114

12-
cmake_minimum_required(VERSION 3.2) # for Hunter
13-
set(CMAKE_POLICY_DEFAULT_CMP0126 OLD) # for Hunter # New in version 3.21
14-
1515
option(HUNTER_NO_TOOLCHAIN_ID_RECALCULATION "No Toolchain-ID recalculation" OFF)
1616

1717
include("cmake/HunterGate.cmake")
@@ -106,4 +106,7 @@ target_include_directories(WebServer PRIVATE ${RAPIDJSON_INCLUDE_DIRS})
106106

107107
target_link_libraries(WebServer PRIVATE Crow)
108108

109+
# Link to CRT statically in MSVC:
110+
set_property(TARGET WebServer PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
111+
109112
# ====================================

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Sergey Kolomenkin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
An HTTP web server with a REST API for persistent key-value storage.
44
Collects read-write statistics for the entire storage and for each key.
55

6-
## Test Task for a C++ Programmer
6+
**Language**: `C++20`
7+
**Dependencies**: `Boost v1.78.0`, `RapidJSON v1.1.0-b557259-p0`, `CrowCpp v1.0+2`
8+
**Software requirements**: `CMake 3.2+`, C++20 compatible compiler, `Python 3.7+`
9+
10+
## Test Task Description
711

812
You need to write two applications, a client and a server, that communicate with each other.
913

@@ -172,7 +176,7 @@ Reply body example:
172176
CPU Intel Core i5 (8th gen), mobile version, 8 logical cores.
173177
Visual Studio 2019 (v16.11.13), Release build
174178
Windows 10 Version 21H1 (Build 19043.1645)
175-
Number of HTTP server threads: 8
179+
Number of HTTP server threads: 8
176180
Logging of every request is disabled for both server and client side.
177181

178182
### Results

client.py

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
]
2424

2525
COUNT_OF_OPERATIONS_PER_PROCESS = 10_000
26-
COUNT_OF_PROCESSES = multiprocessing.cpu_count()
26+
COUNT_OF_PROCESSES = 1 # multiprocessing.cpu_count()
2727

2828
PERCENT_OF_WRITES = 1
2929

30-
LOG_EVERY_REQUEST = False
30+
LOG_EVERY_REQUEST = True
3131

3232
# =======================================
3333

@@ -47,27 +47,37 @@ def one_request(conn: http.client.HTTPConnection) -> None:
4747
'Accept': 'application/json',
4848
}
4949

50-
if writing:
51-
new_value = f'val_{random.randint(1000, 1000_000_000)}'
50+
while True: # retry connection loop
51+
try:
52+
if writing:
53+
new_value = f'val_{random.randint(1000, 1000_000_000)}'
5254

53-
data = {"value": new_value}
55+
data = {"value": new_value}
5456

55-
if LOG_EVERY_REQUEST:
56-
print('====================================================')
57-
print(f'POST {url}: {data}')
57+
if LOG_EVERY_REQUEST:
58+
print('====================================================')
59+
print(f'POST {url}: {data}')
5860

59-
request_body_bytes = json.dumps(data, indent=2, sort_keys=True).encode(encoding='utf8')
60-
conn.request(method='POST', url=url, body=request_body_bytes, headers=headers_post)
61-
else:
62-
if LOG_EVERY_REQUEST:
63-
print(f'GET {url}')
64-
conn.request(method='GET', url=url, headers=headers_get)
61+
request_body_bytes = json.dumps(data, indent=2, sort_keys=True).encode(encoding='utf8')
62+
conn.request(method='POST', url=url, body=request_body_bytes, headers=headers_post)
63+
else:
64+
if LOG_EVERY_REQUEST:
65+
print(f'GET {url}')
66+
conn.request(method='GET', url=url, headers=headers_get)
6567

66-
response = conn.getresponse()
67-
if LOG_EVERY_REQUEST:
68-
print(f'REPLY: {response.status} {response.reason}')
68+
response = conn.getresponse()
69+
if LOG_EVERY_REQUEST:
70+
print(f'REPLY: {response.status} {response.reason}')
6971

70-
body_bytes = response.read()
72+
body_bytes = response.read()
73+
74+
break # exit infinite retry connection loop
75+
except ConnectionError as error:
76+
print('Failed making HTTP request: ', repr(error))
77+
conn.close()
78+
sleep_duration_sec = 5.0
79+
print(f'Retry after sleeping {sleep_duration_sec} seconds...')
80+
time.sleep(sleep_duration_sec)
7181

7282
if response.status not in (200, 404):
7383
print(f'RAW_REPLY_BODY: {body_bytes}')
@@ -113,10 +123,10 @@ def main() -> None:
113123
print('main: end')
114124

115125

116-
def main_owner() -> None:
117-
print(f'main_owner: start {COUNT_OF_PROCESSES} child processes')
126+
def spawn_processes(process_count) -> None:
127+
print(f'spawn_processes: start {process_count} child processes')
118128

119-
pool = [multiprocessing.Process(target=main, args=()) for _ in range(COUNT_OF_PROCESSES)]
129+
pool = [multiprocessing.Process(target=main, args=()) for _ in range(process_count)]
120130

121131
begin = time.perf_counter()
122132

@@ -125,17 +135,20 @@ def main_owner() -> None:
125135

126136
for process in pool:
127137
process.join()
128-
print('Child process finished')
138+
print('spawn_processes: Child process finished')
129139

130140
end = time.perf_counter()
131141
elapsed = max(end - begin, 0.001)
132-
operation_count = COUNT_OF_OPERATIONS_PER_PROCESS * COUNT_OF_PROCESSES
142+
operation_count = COUNT_OF_OPERATIONS_PER_PROCESS * process_count
133143
ops_per_sec = int(operation_count / elapsed)
134144

135-
print(f'main_owner: It took {elapsed:.3f} seconds to execute {operation_count} requests; {ops_per_sec} requests per second')
145+
print(f'spawn_processes: It took {elapsed:.3f} seconds to execute {operation_count} requests; {ops_per_sec} requests per second')
136146

137-
print('main_owner: end')
147+
print('spawn_processes: end')
138148

139149

140150
if __name__ == '__main__':
141-
main_owner()
151+
if COUNT_OF_PROCESSES == 1:
152+
main()
153+
else:
154+
spawn_processes(COUNT_OF_PROCESSES)

main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int main(void)
2222
const std::string listenHost = "127.0.0.1";
2323
const std::uint16_t listenPort = 8000;
2424
const std::string databaseFilename = "database.json";
25-
const bool logEachRequest = false;
25+
const bool logEachRequest = true;
2626

2727
// =========================================================
2828

0 commit comments

Comments
 (0)