Skip to content

Commit 634c47a

Browse files
committed
avoid repeatedly acquiring and releasing connections when running a single SQL file
... and return better errors when we are getting too many requests
1 parent 24e93e1 commit 634c47a

File tree

3 files changed

+14
-1
lines changed

3 files changed

+14
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
- Fix the default index page in MySQL. Fixes [#23](https://github.com/lovasoa/SQLpage/issues/23).
1111
- Add a new [map](https://sql.ophir.dev/documentation.sql?component=map#component) component to display a map with markers on it. Useful to display geographic data from PostGIS or Spatialite.
1212
- Add a new `icon` attribute to the [table](https://sql.ophir.dev/documentation.sql?component=table#component) component to display icons in the table.
13+
- Fix `textarea` fields in the [form](https://sql.ophir.dev/documentation.sql?component=table#component) component to display the provided `value` attribute. Thanks Frank for the contribution !
14+
- SQLPage now guarantees that a single web request will be handled by a single database connection. Previously, connections were repeatedly taken and put back to the connection pool between each statement, preventing the use of temporary tables, transactions, and other connection-specific features such as [`last_insert_rowid`](https://www.sqlite.org/lang_corefunc.html#last_insert_rowid). This makes it much easier to keep state between SQL statements in a single `.sql` file. Please report any performance regression you might encounter. See [the many-to-many relationship example](./examples/modeling%20a%20many%20to%20many%20relationship%20with%20a%20form/).
1315

1416
## 0.7.2 (2023-07-10)
1517

src/webserver/database/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,14 @@ pub async fn stream_query_results_direct<'a>(
117117
request: &'a RequestInfo,
118118
) -> anyhow::Result<BoxStream<'a, anyhow::Result<Either<AnyQueryResult, AnyRow>>>> {
119119
Ok(async_stream::stream! {
120+
let mut connection = db.connection.acquire().await
121+
.with_context(|| anyhow::anyhow!("Unable to acquire a database connection to execute the SQL file. All of the {} {:?} connections are busy.", db.connection.size(), db.connection.any_kind()))?;
120122
for res in &sql_file.statements {
121123
match res {
122124
Ok(stmt)=>{
123125
let query = bind_parameters(stmt, request)
124126
.with_context(|| format!("Unable to bind parameters to the SQL statement: {stmt}"))?;
125-
let mut stream = query.fetch_many(&db.connection);
127+
let mut stream = query.fetch_many(&mut connection);
126128
while let Some(elem) = stream.next().await {
127129
yield elem.with_context(|| format!("Error while running SQL: {stmt}"))
128130
}

src/webserver/http.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ fn send_anyhow_error(e: &anyhow::Error, resp_send: tokio::sync::oneshot::Sender<
253253
));
254254
}
255255
};
256+
if let Some(sqlx::Error::PoolTimedOut) = e.downcast_ref() {
257+
// People are HTTP connections faster than we can open SQL connections. Ask them to slow down politely.
258+
use rand::Rng;
259+
*resp.status_mut() = StatusCode::SERVICE_UNAVAILABLE;
260+
resp.headers_mut().insert(
261+
header::RETRY_AFTER,
262+
header::HeaderValue::from(rand::thread_rng().gen_range(1..=15)),
263+
);
264+
}
256265
resp_send
257266
.send(resp)
258267
.unwrap_or_else(|_| log::error!("could not send headers"));

0 commit comments

Comments
 (0)