Skip to content

Commit 87fb9e0

Browse files
committed
Initial import
0 parents  commit 87fb9e0

File tree

7 files changed

+8049
-0
lines changed

7 files changed

+8049
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.swp
2+
http_test

COPYING

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM ubuntu:bionic AS builder
2+
RUN apt-get -y update && apt-get install -y postgresql-client build-essential clang libpq-dev
3+
COPY . /app
4+
WORKDIR /app
5+
RUN make
6+
7+
FROM ubuntu:bionic
8+
RUN apt-get -y update && apt-get install -y postgresql-client && rm -rf /var/lib/apt/lists/*
9+
COPY --from=builder /app/http_test /app/http_test
10+
CMD ["/app/http_test"]
11+

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
http_test: http_test.cpp
2+
clang++ -o http_test -std=c++17 http_test.cpp -I$$(pg_config --includedir) $$(pg_config --ldflags) -lpq -lpthread
3+
4+
fmt:
5+
clang-format -i http_test.cpp

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Simple HTTP stress testing
2+
3+
This small C++ software has only one final scope: to have a simple backend to
4+
be used to test the speed of PostgreSQL workloads when a new connection is
5+
established per every request.
6+
7+
8+
## Configuration
9+
10+
Environment variables are your friends:
11+
12+
* `DATABASE_URL` is used as a connection string (libpq is being used to
13+
communicate with the PostgreSQL server);
14+
15+
* `SQL_QUERY` is the SQL query that will be executed when accessing the `/tx`
16+
endpoint.
17+
18+
19+
## Other considerations
20+
21+
As previously said, a new connection is established per request, without any
22+
connection pool. Use pgbouncer if you need something like that.
23+
24+
## Thanks
25+
26+
https://github.com/yhirose/cpp-httplib is a great tool, really.

http_test.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* This program is free software: you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation, either version 3 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* GNU General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU General Public License
13+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
**/
15+
16+
#include "httplib.h"
17+
#include <chrono>
18+
#include <ctime>
19+
#include <iostream>
20+
#include <libpq-fe.h>
21+
22+
void tx(const httplib::Request &req, httplib::Response &res);
23+
void logger(const httplib::Request &req, const httplib::Response &res);
24+
std::string get_configuration(const std::string &env_name,
25+
const std::string &default_value = "");
26+
27+
/**
28+
* The database URL to be used as a libpq connection string
29+
*/
30+
const std::string database_url = get_configuration("DATABASE_URL");
31+
const std::string sql_query = get_configuration("SQL_QUERY", "SELECT 1");
32+
33+
/**
34+
* Everything starts from here
35+
*/
36+
int main() {
37+
// Initial logging
38+
std::cout << "Database URL: " << std::quoted(database_url) << std::endl;
39+
std::cout << "SQL query: " << std::quoted(sql_query) << std::endl;
40+
std::cout.flush();
41+
42+
// Starting HTTP server
43+
httplib::Server svr;
44+
svr.set_logger(logger);
45+
svr.Get("/tx", tx);
46+
svr.listen("0.0.0.0", 8080);
47+
48+
return 0;
49+
}
50+
51+
/**
52+
* Given a request/response pair, put a small log message in stdout
53+
*/
54+
void logger(const httplib::Request &req, const httplib::Response &res) {
55+
using namespace std;
56+
57+
const auto now = std::chrono::system_clock::now();
58+
const auto nowAsTimeT = std::chrono::system_clock::to_time_t(now);
59+
const auto nowMs = std::chrono::duration_cast<std::chrono::milliseconds>(
60+
now.time_since_epoch()) %
61+
1000;
62+
63+
cout << std::put_time(std::localtime(&nowAsTimeT), "%a %b %d %Y %T") << '.'
64+
<< std::setfill('0') << std::setw(3) << nowMs.count();
65+
cout << " " << req.remote_addr << " " << req.path << " - " << res.status
66+
<< endl;
67+
cout.flush();
68+
}
69+
70+
/**
71+
* The test transaction on PostgreSQL
72+
*/
73+
void tx(const httplib::Request &req, httplib::Response &res) {
74+
PGconn *conn = PQconnectdb(database_url.c_str());
75+
76+
if (PQstatus(conn) != CONNECTION_OK) {
77+
res.set_content("Connection error", "text/plain");
78+
res.status = 500;
79+
std::cout << PQerrorMessage(conn) << std::endl;
80+
std::cout.flush();
81+
} else {
82+
PGresult *pgres = PQexec(conn, sql_query.c_str());
83+
if (PQresultStatus(pgres) != PGRES_COMMAND_OK &&
84+
PQresultStatus(pgres) != PGRES_TUPLES_OK) {
85+
res.set_content("Query error", "text/plain");
86+
res.status = 500;
87+
std::cout << PQresultErrorMessage(pgres) << std::endl;
88+
std::cout.flush();
89+
} else {
90+
res.set_content("Ok!", "text/plain");
91+
}
92+
PQclear(pgres);
93+
}
94+
95+
PQfinish(conn);
96+
}
97+
98+
/**
99+
* Get a configuration parameter or returns the default value
100+
*/
101+
std::string get_configuration(const std::string &env_name,
102+
const std::string &default_value) {
103+
auto value = std::getenv(env_name.c_str());
104+
if (value == NULL) {
105+
return default_value;
106+
} else {
107+
return value;
108+
}
109+
}

0 commit comments

Comments
 (0)