Skip to content

Commit 6513d20

Browse files
authored
Add Postgres cache database (#1607)
1 parent 2fd9337 commit 6513d20

File tree

25 files changed

+1012
-520
lines changed

25 files changed

+1012
-520
lines changed

.github/workflows/build.yml

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@ jobs:
2222
env:
2323
BUILD_MODE: debug
2424
RUST_BACKTRACE: 1
25+
services:
26+
redis:
27+
image: redis
28+
ports:
29+
- 6379:6379
30+
options: >-
31+
--health-cmd "redis-cli ping"
32+
--health-interval 10s
33+
--health-timeout 5s
34+
--health-retries 5
35+
postgres:
36+
image: postgres
37+
env:
38+
POSTGRES_USER: postgres
39+
POSTGRES_PASSWORD: pass
40+
POSTGRES_DB: nautilus
41+
ports:
42+
- 5432:5432
43+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
2544

2645
steps:
2746
- name: Free disk space (Ubuntu)
@@ -105,10 +124,16 @@ jobs:
105124
# pre-commit run --hook-stage manual gitlint-ci
106125
pre-commit run --all-files
107126
108-
- name: Install Redis (Linux)
127+
- name: Install Nautilus CLI and run init postgres
109128
run: |
110-
sudo apt-get install redis-server
111-
redis-server --daemonize yes
129+
make install-cli
130+
nautilus database init --schema ${{ github.workspace }}/schema
131+
env:
132+
POSTGRES_HOST: localhost
133+
POSTGRES_PORT: 5432
134+
POSTGRES_USERNAME: postgres
135+
POSTGRES_PASSWORD: pass
136+
POSTGRES_DATABASE: nautilus
112137

113138
- name: Run nautilus_core cargo tests (Linux)
114139
run: |
@@ -224,6 +249,25 @@ jobs:
224249
env:
225250
BUILD_MODE: debug
226251
RUST_BACKTRACE: 1
252+
services:
253+
redis:
254+
image: redis
255+
ports:
256+
- 6379:6379
257+
options: >-
258+
--health-cmd "redis-cli ping"
259+
--health-interval 10s
260+
--health-timeout 5s
261+
--health-retries 5
262+
postgres:
263+
image: postgres
264+
env:
265+
POSTGRES_USER: postgres
266+
POSTGRES_PASSWORD: pass
267+
POSTGRES_DB: nautilus
268+
ports:
269+
- 5432:5432
270+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
227271

228272
steps:
229273
- name: Checkout repository
@@ -290,10 +334,16 @@ jobs:
290334
# pre-commit run --hook-stage manual gitlint-ci
291335
pre-commit run --all-files
292336
293-
- name: Install Redis (macOS)
337+
- name: Install Nautilus CLI and run init postgres
294338
run: |
295-
brew install redis
296-
redis-server --daemonize yes
339+
make install-cli
340+
nautilus database init --schema ${{ github.workspace }}/schema
341+
env:
342+
POSTGRES_HOST: localhost
343+
POSTGRES_PORT: 5432
344+
POSTGRES_USERNAME: postgres
345+
POSTGRES_PASSWORD: pass
346+
POSTGRES_DATABASE: nautilus
297347

298348
- name: Run nautilus_core cargo tests (macOS)
299349
run: |

nautilus_core/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ path = "src/bin/cli.rs"
1414
nautilus-common = { path = "../common"}
1515
nautilus-model = { path = "../model" }
1616
nautilus-core = { path = "../core" }
17-
nautilus-infrastructure = { path = "../infrastructure" , features = ['sql']}
17+
nautilus-infrastructure = { path = "../infrastructure" , features = ['postgres']}
1818
anyhow = { workspace = true }
1919
tokio = {workspace = true}
2020
log = { workspace = true }

nautilus_core/cli/src/database/postgres.rs

Lines changed: 4 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -13,182 +13,12 @@
1313
// limitations under the License.
1414
// -------------------------------------------------------------------------------------------------
1515

16-
use log::{error, info};
17-
use nautilus_infrastructure::sql::pg::{connect_pg, get_postgres_connect_options};
18-
use sqlx::PgPool;
16+
use nautilus_infrastructure::sql::pg::{
17+
connect_pg, drop_postgres, get_postgres_connect_options, init_postgres,
18+
};
1919

2020
use crate::opt::{DatabaseCommand, DatabaseOpt};
2121

22-
/// Scans current path with keyword `nautilus_trader` and build schema dir
23-
fn get_schema_dir() -> anyhow::Result<String> {
24-
std::env::var("SCHEMA_DIR").or_else(|_| {
25-
let nautilus_git_repo_name = "nautilus_trader";
26-
let binding = std::env::current_dir().unwrap();
27-
let current_dir = binding.to_str().unwrap();
28-
match current_dir.find(nautilus_git_repo_name){
29-
Some(index) => {
30-
let schema_path = current_dir[0..index + nautilus_git_repo_name.len()].to_string() + "/schema";
31-
Ok(schema_path)
32-
}
33-
None => anyhow::bail!("Could not calculate schema dir from current directory path or SCHEMA_DIR env variable")
34-
}
35-
})
36-
}
37-
38-
pub async fn init_postgres(pg: &PgPool, database: String, password: String) -> anyhow::Result<()> {
39-
info!("Initializing Postgres database with target permissions and schema");
40-
// create public schema
41-
match sqlx::query("CREATE SCHEMA IF NOT EXISTS public;")
42-
.execute(pg)
43-
.await
44-
{
45-
Ok(_) => info!("Schema public created successfully"),
46-
Err(err) => error!("Error creating schema public: {:?}", err),
47-
}
48-
// create role if not exists
49-
match sqlx::query(format!("CREATE ROLE {database} PASSWORD '{password}' LOGIN;").as_str())
50-
.execute(pg)
51-
.await
52-
{
53-
Ok(_) => info!("Role {} created successfully", database),
54-
Err(err) => {
55-
if err.to_string().contains("already exists") {
56-
info!("Role {} already exists", database);
57-
} else {
58-
error!("Error creating role {}: {:?}", database, err);
59-
}
60-
}
61-
}
62-
// execute all the sql files in schema dir
63-
let schema_dir = get_schema_dir()?;
64-
let mut sql_files =
65-
std::fs::read_dir(schema_dir)?.collect::<Result<Vec<_>, std::io::Error>>()?;
66-
for file in &mut sql_files {
67-
let file_name = file.file_name();
68-
info!("Executing schema file: {:?}", file_name);
69-
let file_path = file.path();
70-
let sql_content = std::fs::read_to_string(file_path.clone())?;
71-
for sql_statement in sql_content.split(';').filter(|s| !s.trim().is_empty()) {
72-
sqlx::query(sql_statement).execute(pg).await?;
73-
}
74-
}
75-
// grant connect
76-
match sqlx::query(format!("GRANT CONNECT ON DATABASE {database} TO {database};").as_str())
77-
.execute(pg)
78-
.await
79-
{
80-
Ok(_) => info!("Connect privileges granted to role {}", database),
81-
Err(err) => error!(
82-
"Error granting connect privileges to role {}: {:?}",
83-
database, err
84-
),
85-
}
86-
// grant all schema privileges to the role
87-
match sqlx::query(format!("GRANT ALL PRIVILEGES ON SCHEMA public TO {database};").as_str())
88-
.execute(pg)
89-
.await
90-
{
91-
Ok(_) => info!("All schema privileges granted to role {}", database),
92-
Err(err) => error!(
93-
"Error granting all privileges to role {}: {:?}",
94-
database, err
95-
),
96-
}
97-
// grant all table privileges to the role
98-
match sqlx::query(
99-
format!("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO {database};").as_str(),
100-
)
101-
.execute(pg)
102-
.await
103-
{
104-
Ok(_) => info!("All tables privileges granted to role {}", database),
105-
Err(err) => error!(
106-
"Error granting all privileges to role {}: {:?}",
107-
database, err
108-
),
109-
}
110-
// grant all sequence privileges to the role
111-
match sqlx::query(
112-
format!("GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO {database};").as_str(),
113-
)
114-
.execute(pg)
115-
.await
116-
{
117-
Ok(_) => info!("All sequences privileges granted to role {}", database),
118-
Err(err) => error!(
119-
"Error granting all privileges to role {}: {:?}",
120-
database, err
121-
),
122-
}
123-
// grant all function privileges to the role
124-
match sqlx::query(
125-
format!("GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO {database};").as_str(),
126-
)
127-
.execute(pg)
128-
.await
129-
{
130-
Ok(_) => info!("All functions privileges granted to role {}", database),
131-
Err(err) => error!(
132-
"Error granting all privileges to role {}: {:?}",
133-
database, err
134-
),
135-
}
136-
137-
Ok(())
138-
}
139-
140-
pub async fn drop_postgres(pg: &PgPool, database: String) -> anyhow::Result<()> {
141-
// execute drop owned
142-
match sqlx::query(format!("DROP OWNED BY {database}").as_str())
143-
.execute(pg)
144-
.await
145-
{
146-
Ok(_) => info!("Dropped owned objects by role {}", database),
147-
Err(err) => error!("Error dropping owned by role {}: {:?}", database, err),
148-
}
149-
// revoke connect
150-
match sqlx::query(format!("REVOKE CONNECT ON DATABASE {database} FROM {database};").as_str())
151-
.execute(pg)
152-
.await
153-
{
154-
Ok(_) => info!("Revoked connect privileges from role {}", database),
155-
Err(err) => error!(
156-
"Error revoking connect privileges from role {}: {:?}",
157-
database, err
158-
),
159-
}
160-
// revoke privileges
161-
match sqlx::query(
162-
format!("REVOKE ALL PRIVILEGES ON DATABASE {database} FROM {database};").as_str(),
163-
)
164-
.execute(pg)
165-
.await
166-
{
167-
Ok(_) => info!("Revoked all privileges from role {}", database),
168-
Err(err) => error!(
169-
"Error revoking all privileges from role {}: {:?}",
170-
database, err
171-
),
172-
}
173-
// execute drop schema
174-
match sqlx::query("DROP SCHEMA IF EXISTS public CASCADE")
175-
.execute(pg)
176-
.await
177-
{
178-
Ok(_) => info!("Dropped schema public"),
179-
Err(err) => error!("Error dropping schema public: {:?}", err),
180-
}
181-
// drop role
182-
match sqlx::query(format!("DROP ROLE IF EXISTS {database};").as_str())
183-
.execute(pg)
184-
.await
185-
{
186-
Ok(_) => info!("Dropped role {}", database),
187-
Err(err) => error!("Error dropping role {}: {:?}", database, err),
188-
}
189-
Ok(())
190-
}
191-
19222
pub async fn run_database_command(opt: DatabaseOpt) -> anyhow::Result<()> {
19323
let command = opt.command.clone();
19424

@@ -207,6 +37,7 @@ pub async fn run_database_command(opt: DatabaseOpt) -> anyhow::Result<()> {
20737
&pg,
20838
pg_connect_options.database,
20939
pg_connect_options.password,
40+
config.schema,
21041
)
21142
.await?;
21243
}

nautilus_core/cli/src/opt.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ pub struct DatabaseConfig {
5151
/// Password for connecting to the database
5252
#[arg(long)]
5353
pub password: Option<String>,
54+
/// Directory path to the schema files
55+
#[arg(long)]
56+
pub schema: Option<String>,
5457
}
5558

5659
#[derive(Parser, Debug, Clone)]

nautilus_core/infrastructure/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ extension-module = [
5050
]
5151
python = ["pyo3"]
5252
redis = ["dep:redis"]
53-
sql = ["dep:sqlx"]
53+
postgres = ["dep:sqlx"]

nautilus_core/infrastructure/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ pub mod python;
3434
#[cfg(feature = "redis")]
3535
pub mod redis;
3636

37-
#[cfg(feature = "sql")]
37+
#[cfg(feature = "postgres")]
3838
pub mod sql;

nautilus_core/infrastructure/src/python/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#[cfg(feature = "redis")]
1919
pub mod redis;
2020

21+
#[cfg(feature = "postgres")]
22+
pub mod sql;
23+
2124
use pyo3::{prelude::*, pymodule};
2225

2326
#[pymodule]
@@ -26,5 +29,7 @@ pub fn infrastructure(_: Python<'_>, m: &PyModule) -> PyResult<()> {
2629
m.add_class::<crate::redis::cache::RedisCacheDatabase>()?;
2730
#[cfg(feature = "redis")]
2831
m.add_class::<crate::redis::msgbus::RedisMessageBusDatabase>()?;
32+
#[cfg(feature = "postgres")]
33+
m.add_class::<crate::sql::cache_database::PostgresCacheDatabase>()?;
2934
Ok(())
3035
}

0 commit comments

Comments
 (0)