1
0
mirror of https://github.com/actix/examples synced 2025-06-28 09:50:36 +02:00

Restructure folders (#411)

This commit is contained in:
Daniel T. Rodrigues
2021-02-25 21:57:58 -03:00
committed by GitHub
parent 9db98162b2
commit c3407627d0
334 changed files with 127 additions and 120 deletions

View File

@ -0,0 +1,17 @@
[package]
name = "async_db"
version = "2.0.0"
authors = ["Darin Gordon <dkcdkg@gmail.com>"]
edition = "2018"
[dependencies]
actix-web = "3"
env_logger = "0.8"
failure = "0.1.7"
futures = "0.3.1"
num_cpus = "1.13"
r2d2 = "0.8.2"
r2d2_sqlite = "0.14"
rusqlite = "0.21"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -0,0 +1,47 @@
Getting started using databases with Actix web, asynchronously.
## Usage
### init database sqlite
From the root directory of this project:
```bash
bash db/setup_db.sh
```
This creates a sqlite database, weather.db, in the root.
### server
```bash
# if ubuntu : sudo apt-get install libsqlite3-dev
# if fedora : sudo dnf install libsqlite3x-devel
cargo run (or ``cargo watch -x run``)
# Started http server: 127.0.0.1:8080
```
### web client
[http://127.0.0.1:8080/asyncio_weather](http://127.0.0.1:8080/asyncio_weather)
[http://127.0.0.1:8080/parallel_weather](http://127.0.0.1:8080/parallel_weather)
### sqlite client
```bash
# if ubuntu : sudo apt-get install sqlite3
# if fedora : sudo dnf install sqlite3x
sqlite3 weather.db
sqlite> .tables
sqlite> select * from nyc_weather;
```
## Dependencies
On Ubuntu 19.10:
```
sudo apt install libsqlite3-dev
```

Binary file not shown.

View File

@ -0,0 +1,5 @@
This directory includes weather information obtained from NOAA for NYC Central Park: https://www.ncdc.noaa.gov/cdo-web/
# Setup Instructions
Set up a sqlite3 database by executing the setup_db.sh file: `bash sqlite_db.sh`

View File

@ -0,0 +1,28 @@
CREATE TABLE nyc_weather(
STATION TEXT,
NAME TEXT,
DATE TEXT,
ACMH DOUBLE,
AWND DOUBLE,
FMTM DOUBLE,
PGTM DOUBLE,
PRCP DOUBLE,
PSUN DOUBLE,
SNOW DOUBLE,
SNWD DOUBLE,
TAVG DOUBLE,
TMAX DOUBLE,
TMIN DOUBLE,
TSUN DOUBLE,
WDF1 DOUBLE,
WDF2 DOUBLE,
WDF5 DOUBLE,
WDFG DOUBLE,
WDFM DOUBLE,
WSF1 DOUBLE,
WSF2 DOUBLE,
WSF5 DOUBLE,
WSFG DOUBLE,
WSFM DOUBLE
);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd $(dirname "$0")
sqlite3 ../weather.db < db.sql
sqlite3 -csv ../weather.db ".import nyc_centralpark_weather.csv nyc_weather"

View File

@ -0,0 +1,122 @@
use actix_web::{web, Error as AWError};
use failure::Error;
use futures::{Future, TryFutureExt};
use rusqlite::{Statement, NO_PARAMS};
use serde::{Deserialize, Serialize};
use std::{thread::sleep, time::Duration};
pub type Pool = r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>;
pub type Connection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
type WeatherAggResult = Result<Vec<WeatherAgg>, rusqlite::Error>;
#[derive(Debug, Serialize, Deserialize)]
pub enum WeatherAgg {
AnnualAgg { year: i32, total: f64 },
MonthAgg { year: i32, month: i32, total: f64 },
}
pub enum Queries {
GetTopTenHottestYears,
GetTopTenColdestYears,
GetTopTenHottestMonths,
GetTopTenColdestMonths,
}
pub fn execute(
pool: &Pool,
query: Queries,
) -> impl Future<Output = Result<Vec<WeatherAgg>, AWError>> {
let pool = pool.clone();
web::block(move || {
// simulate an expensive query, see comments at top of main.rs
sleep(Duration::from_secs(2));
let result = match query {
Queries::GetTopTenHottestYears => get_hottest_years(pool.get()?),
Queries::GetTopTenColdestYears => get_coldest_years(pool.get()?),
Queries::GetTopTenHottestMonths => get_hottest_months(pool.get()?),
Queries::GetTopTenColdestMonths => get_coldest_months(pool.get()?),
};
result.map_err(Error::from)
})
.map_err(AWError::from)
}
fn get_hottest_years(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"
SELECT cast(strftime('%Y', date) as int) as theyear,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear
ORDER BY total DESC LIMIT 10",
)?;
get_rows_as_annual_agg(stmt)
}
fn get_coldest_years(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"
SELECT cast(strftime('%Y', date) as int) as theyear,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear
ORDER BY total ASC LIMIT 10",
)?;
get_rows_as_annual_agg(stmt)
}
fn get_rows_as_annual_agg(mut statement: Statement) -> WeatherAggResult {
statement
.query_map(NO_PARAMS, |row| {
Ok(WeatherAgg::AnnualAgg {
year: row.get(0)?,
total: row.get(1)?,
})
})
.and_then(Iterator::collect)
}
fn get_hottest_months(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"SELECT cast(strftime('%Y', date) as int) as theyear,
cast(strftime('%m', date) as int) as themonth,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear, themonth
ORDER BY total DESC LIMIT 10",
)?;
get_rows_as_month_agg(stmt)
}
fn get_coldest_months(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"SELECT cast(strftime('%Y', date) as int) as theyear,
cast(strftime('%m', date) as int) as themonth,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear, themonth
ORDER BY total ASC LIMIT 10",
)?;
get_rows_as_month_agg(stmt)
}
fn get_rows_as_month_agg(mut statement: Statement) -> WeatherAggResult {
statement
.query_map(NO_PARAMS, |row| {
Ok(WeatherAgg::MonthAgg {
year: row.get(0)?,
month: row.get(1)?,
total: row.get(2)?,
})
})
.and_then(Iterator::collect)
}

View File

@ -0,0 +1,77 @@
/* Actix-Web Asynchronous Database Example
This project illustrates expensive and blocking database requests that runs
in a thread-pool using `web::block` with two examples:
1. An asynchronous handler that executes 4 queries in *sequential order*,
collecting the results and returning them as a single serialized json object
2. An asynchronous handler that executes 4 queries in *parallel*,
collecting the results and returning them as a single serialized json object
Note: The use of sleep(Duration::from_secs(2)); in db.rs is to make performance
improvement with parallelism more obvious.
*/
use std::io;
use actix_web::{middleware, web, App, Error as AWError, HttpResponse, HttpServer};
use futures::future::join_all;
use r2d2_sqlite::{self, SqliteConnectionManager};
mod db;
use db::{Pool, Queries};
/// Version 1: Calls 4 queries in sequential order, as an asynchronous handler
#[allow(clippy::eval_order_dependence)] // it's FP?
async fn asyncio_weather(db: web::Data<Pool>) -> Result<HttpResponse, AWError> {
let result = vec![
db::execute(&db, Queries::GetTopTenHottestYears).await?,
db::execute(&db, Queries::GetTopTenColdestYears).await?,
db::execute(&db, Queries::GetTopTenHottestMonths).await?,
db::execute(&db, Queries::GetTopTenColdestMonths).await?,
];
Ok(HttpResponse::Ok().json(result))
}
/// Version 2: Calls 4 queries in parallel, as an asynchronous handler
/// Returning Error types turn into None values in the response
async fn parallel_weather(db: web::Data<Pool>) -> Result<HttpResponse, AWError> {
let fut_result = vec![
Box::pin(db::execute(&db, Queries::GetTopTenHottestYears)),
Box::pin(db::execute(&db, Queries::GetTopTenColdestYears)),
Box::pin(db::execute(&db, Queries::GetTopTenHottestMonths)),
Box::pin(db::execute(&db, Queries::GetTopTenColdestMonths)),
];
let result: Result<Vec<_>, _> = join_all(fut_result).await.into_iter().collect();
Ok(HttpResponse::Ok().json(result.map_err(AWError::from)?))
}
#[actix_web::main]
async fn main() -> io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
// Start N db executor actors (N = number of cores avail)
let manager = SqliteConnectionManager::file("weather.db");
let pool = Pool::new(manager).unwrap();
// Start http server
HttpServer::new(move || {
App::new()
// store db pool as Data object
.data(pool.clone())
.wrap(middleware::Logger::default())
.service(
web::resource("/asyncio_weather").route(web::get().to(asyncio_weather)),
)
.service(
web::resource("/parallel_weather")
.route(web::get().to(parallel_weather)),
)
})
.bind("127.0.0.1:8080")?
.run()
.await
}