Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transaction deferable #2

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
53b4e79
transaction deferable
insani7y Feb 28, 2024
2b0f317
transaction deferable
insani7y Feb 28, 2024
9559346
transaction deferable
insani7y Feb 28, 2024
b5c9c4a
transaction deferable
insani7y Feb 29, 2024
10ea9c6
transaction deferable
insani7y Feb 29, 2024
2fc6959
readme
insani7y Mar 3, 2024
3ad9f35
readme
insani7y Mar 3, 2024
ad6f701
fix clippy
insani7y Mar 3, 2024
f4a7112
Added test actions
chandr-andr Feb 21, 2024
cc7a8e9
Removed unnecessary code from actions
chandr-andr Feb 21, 2024
e140dff
Set line length to 79
chandr-andr Feb 21, 2024
6d5e1c9
Added more tests
chandr-andr Feb 23, 2024
675ae9d
Continue adding tests
chandr-andr Feb 25, 2024
f02cd98
Continue adding tests
chandr-andr Feb 28, 2024
67e5a47
Continue adding tests
chandr-andr Feb 28, 2024
68ae8e2
Continue adding tests
chandr-andr Feb 28, 2024
09ff57e
Continue adding tests
chandr-andr Feb 29, 2024
c236a64
Continue adding tests
chandr-andr Feb 29, 2024
2409705
Continue adding tests
chandr-andr Mar 3, 2024
2b0f626
Added dsn support
chandr-andr Mar 3, 2024
c1b84b8
Bumped version to 0.2.1
chandr-andr Mar 3, 2024
f46298b
Added connection recycling settings
chandr-andr Mar 4, 2024
eed31d6
Bumped version to 0.2.2
chandr-andr Mar 4, 2024
5e630f4
Made README more pretty
chandr-andr Mar 4, 2024
2ce0004
Bumped version to 0.2.3
chandr-andr Mar 4, 2024
b038aff
Added small section about benchmarks
chandr-andr Mar 4, 2024
ba41005
transaction deferable
insani7y Feb 29, 2024
8351114
add test
insani7y Mar 5, 2024
af7bed4
add test
insani7y Mar 5, 2024
1004db7
add test
insani7y Mar 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/CI.yml → .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@ name: CI

on:
push:
branches:
- main
- master
tags:
- '*'
pull_request:
workflow_dispatch:

permissions:
contents: read
Expand Down
85 changes: 85 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: 'Testing package'

on:
pull_request:

jobs:
py-lint:
strategy:
matrix:
cmd:
- black
- isort
- ruff
- mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: Run lint check
uses: pre-commit/action@v3.0.0
with:
extra_args: -a ${{ matrix.cmd }}
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
override: true
- name: Check code format
run: cargo fmt -- --check --config use_try_shorthand=true,imports_granularity=Crate

clippy:
permissions:
checks: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: -p psqlpy --all-features -- -W clippy::all -W clippy::pedantic -D warnings
pytest:
name: ${{matrix.job.os}}-${{matrix.py_version}}
strategy:
matrix:
py_version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
job:
- os: ubuntu-latest
ssl_cmd: sudo apt-get update && sudo apt-get install libssl-dev openssl
runs-on: ${{matrix.job.os}}
steps:
- uses: actions/checkout@v1
- uses: ikalnytskyi/action-setup-postgres@v4
with:
username: postgres
password: postgres
database: psqlpy_test
id: postgres
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
override: true
- name: Setup OpenSSL
run: ${{matrix.job.ssl_cmd}}
- name: Setup python for test ${{ matrix.py_version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.py_version }}
- name: Install tox
run: pip install "tox-gh>=1.2,<2"
- name: Run pytest
run: tox -v

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "psqlpy"
version = "0.2.0"
version = "0.2.3"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
118 changes: 109 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/psqlpy?style=for-the-badge)](https://pypi.org/project/psqlpy/)
[![PyPI](https://img.shields.io/pypi/v/psqlpy?style=for-the-badge)](https://pypi.org/project/psqlpy/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/psqlpy?style=for-the-badge)](https://pypistats.org/packages/psqlpy)

# PSQLPy - Async PostgreSQL driver for Python written in Rust.

Driver for PostgreSQL written fully in Rust and exposed to Python.
The project is under active development and _**we cannot confirm that it's ready for production**_. Anyway, We will be grateful for the bugs found and open issues. Stay tuned.
*Normal documentation is in development.*
_Normal documentation is in development._

## Installation

You can install package with `pip` or `poetry`.

poetry:

```bash
> poetry add psqlpy
```

pip:

```bash
> pip install psqlpy
```

Or you can build it by yourself. To do it, install stable rust and [maturin](/~https://github.com/PyO3/maturin).

```
> maturin develop --release
```

## Usage

Usage is as easy as possible.
Create new instance of PSQLPool, startup it and start querying.

```python
from typing import Any

Expand Down Expand Up @@ -52,9 +62,65 @@ async def main() -> None:
# rust does it instead.

```

Please take into account that each new execute gets new connection from connection pool.

### DSN support

You can separate specify `host`, `port`, `username`, etc or specify everything in one `DSN`.
**Please note that if you specify DSN any other argument doesn't take into account.**

```py
from typing import Any

from psqlpy import PSQLPool


db_pool = PSQLPool(
dsn="postgres://postgres:postgres@localhost:5432/postgres",
max_db_pool_size=2,
)

async def main() -> None:
await db_pool.startup()

res: list[dict[str, Any]] = await db_pool.execute(
"SELECT * FROM users",
)

print(res)
# You don't need to close Database Pool by yourself,
# rust does it instead.
```

### Control connection recycling

There are 3 available options to control how a connection is recycled - `Fast`, `Verified` and `Clean`.
As connection can be closed in different situations on various sides you can select preferable behavior of how a connection is recycled.

- `Fast`: Only run `is_closed()` when recycling existing connections.
- `Verified`: Run `is_closed()` and execute a test query. This is slower, but guarantees that the database connection is ready to
be used. Normally, `is_closed()` should be enough to filter
out bad connections, but under some circumstances (i.e. hard-closed
network connections) it's possible that `is_closed()`
returns `false` while the connection is dead. You will receive an error
on your first query then.
- `Clean`: Like [`Verified`] query method, but instead use the following sequence of statements which guarantees a pristine connection:
```sql
CLOSE ALL;
SET SESSION AUTHORIZATION DEFAULT;
RESET ALL;
UNLISTEN *;
SELECT pg_advisory_unlock_all();
DISCARD TEMP;
DISCARD SEQUENCES;
```
This is similar to calling `DISCARD ALL`. but doesn't call
`DEALLOCATE ALL` and `DISCARD PLAN`, so that the statement cache is not
rendered ineffective.

## Query parameters

You can pass parameters into queries.
Parameters can be passed in any `execute` method as the second parameter, it must be a list.
Any placeholder must be marked with `$< num>`.
Expand All @@ -67,7 +133,9 @@ Any placeholder must be marked with `$< num>`.
```

## Connection

You can work with connection instead of DatabasePool.

```python
from typing import Any

Expand Down Expand Up @@ -98,17 +166,22 @@ async def main() -> None:
```

## Transactions

Of course it's possible to use transactions with this driver.
It's as easy as possible and sometimes it copies common functionality from PsycoPG and AsyncPG.

### Transaction parameters

In process of transaction creation it is possible to specify some arguments to configure transaction.

- `isolation_level`: level of the isolation. By default - `None`.
- `read_variant`: read option. By default - `None`.
- `deferable`: deferable option. By default - `None`.

### You can use transactions as async context managers

By default async context manager only begins and commits transaction automatically.

```python
from typing import Any

Expand All @@ -132,6 +205,7 @@ async def main() -> None:
```

### Or you can control transaction fully on your own.

```python
from typing import Any

Expand Down Expand Up @@ -163,9 +237,11 @@ async def main() -> None:
```

### Transactions can be rolled back

You must understand that rollback can be executed only once per transaction.
After it's execution transaction state changes to `done`.
If you want to use `ROLLBACK TO SAVEPOINT`, see below.

```python
from typing import Any

Expand All @@ -191,6 +267,7 @@ async def main() -> None:
```

### Transaction ROLLBACK TO SAVEPOINT

You can rollback your transaction to the specified savepoint, but before it you must create it.

```python
Expand Down Expand Up @@ -224,6 +301,7 @@ async def main() -> None:
```

### Transaction RELEASE SAVEPOINT

It's possible to release savepoint

```python
Expand Down Expand Up @@ -252,12 +330,15 @@ async def main() -> None:
```

## Cursors

Library supports PostgreSQL cursors.

Cursors can be created only in transaction. In addition, cursor supports async iteration.

### Cursor parameters

In process of cursor creation you can specify some configuration parameters.

- `querystring`: query for the cursor. Required.
- `parameters`: parameters for the query. Not Required.
- `fetch_number`: number of records per fetch if cursor is used as an async iterator. If you are using `.fetch()` method you can pass different fetch number. Not required. Default - 10.
Expand Down Expand Up @@ -301,7 +382,9 @@ async def main() -> None:
```

### Cursor operations

Available cursor operations:

- FETCH count - `cursor.fetch(fetch_number=)`
- FETCH NEXT - `cursor.fetch_next()`
- FETCH PRIOR - `cursor.fetch_prior()`
Expand All @@ -314,15 +397,16 @@ Available cursor operations:
- FETCH BACKWARD ALL - `cursor.fetch_backward_all()`

## Extra Types

Sometimes it's impossible to identify which type user tries to pass as a argument. But Rust is a strongly typed programming language so we have to help.

| Extra Type in Python | Type in PostgreSQL | Type in Rust |
| ------------- | ------------- | -------------
| SmallInt | SmallInt | i16 |
| Integer | Integer | i32 |
| BigInt | BigInt | i64 |
| PyUUID | UUID | Uuid |
| PyJSON | JSON, JSONB | Value |
| Extra Type in Python | Type in PostgreSQL | Type in Rust |
| -------------------- | ------------------ | ------------ |
| SmallInt | SmallInt | i16 |
| Integer | Integer | i32 |
| BigInt | BigInt | i64 |
| PyUUID | UUID | Uuid |
| PyJSON | JSON, JSONB | Value |

```python
from typing import Any
Expand Down Expand Up @@ -366,4 +450,20 @@ async def main() -> None:
# You don't need to close Database Pool by yourself,
# rust does it instead.

```
```

## Benchmarks

We have made some benchmark to compare `PSQLPy`, `AsyncPG`, `Psycopg3`.
Main idea is do not compare clear drivers because there are a few situations in which you need to use only driver without any other dependencies.

**So infrastructure consists of:**

1. AioHTTP
2. PostgreSQL driver (`PSQLPy`, `AsyncPG`, `Psycopg3`)
3. PostgreSQL v15. Server is located in other part of the world, because we want to simulate network problems.
4. Grafana (dashboards)
5. InfluxDB
6. JMeter (for load testing)

The results are very promising! `PSQLPy` is faster than `AsyncPG` at best by 2 times, at worst by 45%. `PsycoPG` is 3.5 times slower than `PSQLPy` in the worst case, 60% in the best case.
Loading