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

Persistence enhancements - including adding PostgreSQL support #85

Merged
merged 49 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
167098d
Refactor existing interface ready to slot in CRUD
peterbroadhurst May 25, 2023
6d557f5
Update API for rich query
peterbroadhurst May 26, 2023
ef4c85e
Move over LevelDB to new persistence model for TXHistory and ManagedTX
peterbroadhurst Jun 2, 2023
05db935
Work through base changes to simple policy engine, and some refactor
peterbroadhurst Jun 3, 2023
43352e5
Tweaks to tests for new context structure
peterbroadhurst Jun 4, 2023
4d13b8c
First PSQL collection (checkpoints) and Github action tweaks
peterbroadhurst Jun 6, 2023
fd3b446
Force fetch history to work around difference in action
peterbroadhurst Jun 6, 2023
0fa796a
Tweaks
peterbroadhurst Jun 6, 2023
ae30af3
Set psql hostname for Github action
peterbroadhurst Jun 6, 2023
d36109f
Fix codecov
peterbroadhurst Jun 6, 2023
39e1f2d
Update confirmations callback for incremental writing allowing for st…
peterbroadhurst Jun 7, 2023
b42963e
Work through confirmations API changes to other components
peterbroadhurst Jun 7, 2023
1e4af2d
Interim commit after firefly-common pre-req work, and start of TX writer
peterbroadhurst Jun 8, 2023
e539794
More CRUD definitions, simplication of TXHistory, and work on TX writ…
peterbroadhurst Jun 9, 2023
0a94977
Transaction persistence foundation commit
peterbroadhurst Jun 9, 2023
005fbcb
Work through more scenarios and tests
peterbroadhurst Jun 9, 2023
3bc0d81
More tests
peterbroadhurst Jun 9, 2023
0d12561
Event stream PSQL interface and test
peterbroadhurst Jun 10, 2023
e6ee98e
Listeners collection and more work closing out function on transactions
peterbroadhurst Jun 10, 2023
5210de9
Complete transaction tests
peterbroadhurst Jun 10, 2023
9bf5a0c
More work on history compression and tests
peterbroadhurst Jun 11, 2023
cbf05df
Compaction scheduling for TX History
peterbroadhurst Jun 11, 2023
7492860
Persistence layer dcut
peterbroadhurst Jun 11, 2023
a9dbd30
Add API endpoints for confirmations, history and receipt
peterbroadhurst Jun 11, 2023
f056b26
Get direclty on a TX gives you the TX summary
peterbroadhurst Jun 12, 2023
fb667b7
Restore deleted file
peterbroadhurst Jun 12, 2023
27db400
Parallel receipt querying without blocking
peterbroadhurst Jun 13, 2023
ef05050
Include deprecreated headers on get-many as well as get-single
peterbroadhurst Jun 13, 2023
38f38ff
Handle nil return on TransactionReceipt more gracefully
peterbroadhurst Jun 13, 2023
b8d0ba1
Correct cases where we were dispatching an erroneous confirmation
peterbroadhurst Jun 13, 2023
05334c2
Do not notify on nil
peterbroadhurst Jun 13, 2023
93560ef
Move nonce assignment to insert-time, and make a core part of FFTM
peterbroadhurst Jun 14, 2023
1f942ec
Fix lint
peterbroadhurst Jun 14, 2023
f7c6431
Guard against bad JSON
peterbroadhurst Jun 15, 2023
b034071
Guard against bad JSON
peterbroadhurst Jun 15, 2023
cd06187
Do migration for LevelDB on list as well as get-by-id
peterbroadhurst Jun 21, 2023
88dfa06
Field by field migration (nonce might be on the root in persistence)
peterbroadhurst Jun 21, 2023
f24dc86
Fix txhistory indexes
peterbroadhurst Jun 21, 2023
346cd5c
Idempotency pre-check
peterbroadhurst Jun 22, 2023
8ba70ee
Add insert-time idempotency check and 409 status
peterbroadhurst Jun 22, 2023
fbe040e
Only dispatch events on completed
peterbroadhurst Jun 22, 2023
19bd145
Drop before create on migrations
peterbroadhurst Jun 25, 2023
c3c1deb
Remove mandatory non-null on data, and add RichQuery to toolkit
peterbroadhurst Jun 25, 2023
a87fcb8
Idempotency reject can incorrectly spend a nonce
peterbroadhurst Jun 26, 2023
6295a52
Update common for filtering
peterbroadhurst Jun 30, 2023
c60e912
Fix build
peterbroadhurst Jun 30, 2023
4fd28d4
Update FF Common
peterbroadhurst Jun 30, 2023
259f397
Address review comments
peterbroadhurst Jun 30, 2023
de4c9b7
Update FF common to 1.2.18
peterbroadhurst Jun 30, 2023
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
25 changes: 21 additions & 4 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,36 @@ on:
jobs:
build:
runs-on: ubuntu-latest
container: golang:1.19-bullseye
defaults:
run:
shell: bash # needed for codecov
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: f1refly
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Fetch history # See https://github.com/actions/checkout/issues/477
awrichar marked this conversation as resolved.
Show resolved Hide resolved
run: |-
git config --global --add safe.directory $PWD
git fetch origin

- name: Build and Test
env:
TEST_FLAGS: -v
POSTGRES_HOSTNAME: postgres
POSTGRES_PASSWORD: f1refly
POSTGRES_PORT: 5432
run: make

- name: Upload coverage
Expand Down
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ linters-settings:
gosec:
excludes:
- G402
revive:
rules:
- name: unused-parameter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge deal, but why disable this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me go back to this - in a recent update (I think coupled to 1.19 maybe) unused parameters on all functions have to be renamed down to _. I have been updating all the repos where I find it. I can't remember why I didn't just do that here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I put it back, and it only found one remaining issue. So I probably did it temporarily and forgot to remove it - thanks for the catch 👍

severity: warning
disabled: true
goheader:
values:
regexp:
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"txid",
"txns",
"txtype",
"txwriter",
"unflushed",
"unmarshalled",
"unmarshalling",
Expand All @@ -102,5 +103,5 @@
"wsconfig",
"wsmocks"
],
"go.testTimeout": "10s"
"go.testTimeout": "20s"
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ $(eval $(call makemock, pkg/ffcapi, API, ffc
$(eval $(call makemock, pkg/txhandler, TransactionHandler, txhandlermocks))
$(eval $(call makemock, pkg/txhandler, ManagedTxEventHandler, txhandlermocks))
$(eval $(call makemock, internal/metrics, TransactionHandlerMetrics, metricsmocks))
$(eval $(call makemock, pkg/txhistory, Manager, txhistorymocks))
$(eval $(call makemock, internal/confirmations, Manager, confirmationsmocks))
$(eval $(call makemock, internal/persistence, Persistence, persistencemocks))
$(eval $(call makemock, internal/persistence, TransactionPersistence, persistencemocks))
$(eval $(call makemock, internal/persistence, RichQuery, persistencemocks))
$(eval $(call makemock, internal/ws, WebSocketChannels, wsmocks))
$(eval $(call makemock, internal/ws, WebSocketServer, wsmocks))
$(eval $(call makemock, internal/events, Stream, eventsmocks))
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ One of the most sophisticated parts of the FireFly Connector Framework is the ha

[![Event Streams](./images/fftm_event_streams_architecture.jpg)](./images/fftm_event_streams_architecture.jpg)

# Persistence

Simple filesystem (LevelDB) or remote database (PostgreSQL) persistence is supported.

The SQL based persistence implementation includes some additional features, including:
- Flush-writers for transaction persistence, to optimize database commits when writing new transactions in parallel
- Rich query support on the API

## Running PostgreSQL tests locally

To run the postgres tests, you need to have a local database started as follows:

```bash
docker run -d --name postgres -e POSTGRES_PASSWORD=f1refly -p 5432:5432 postgres
```


# Configuration

See [config.md](./config.md)
39 changes: 38 additions & 1 deletion config.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
|publicURL|External address callers should access API over|`string`|`<nil>`
|readTimeout|The maximum time to wait when reading from an HTTP connection|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`
|shutdownTimeout|The maximum amount of time to wait for any open HTTP requests to finish before shutting down the HTTP server|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
|simpleQuery|Force use of original limited API query syntax, even if rich query is supported in the database|`boolean`|`<nil>`
|writeTimeout|The maximum time to wait when writing to a HTTP connection|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`

## api.auth
Expand Down Expand Up @@ -43,9 +44,18 @@
|---|-----------|----|-------------|
|blockQueueLength|Internal queue length for notifying the confirmations manager of new blocks|`int`|`50`
|notificationQueueLength|Internal queue length for notifying the confirmations manager of new transactions/events|`int`|`50`
|receiptWorkers|Number of workers to use to query in parallel for receipts|`int`|`10`
|required|Number of confirmations required to consider a transaction/event final|`int`|`20`
|staleReceiptTimeout|Duration after which to force a receipt check for a pending transaction|[`time.Duration`](https://pkg.go.dev/time#Duration)|`1m`

## confirmations.retry

|Key|Description|Type|Default Value|
|---|-----------|----|-------------|
|factor|The retry backoff factor|`float32`|`2`
|initialDelay|The initial retry delay|[`time.Duration`](https://pkg.go.dev/time#Duration)|`100ms`
|maxDelay|The maximum retry delay|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`

## cors

|Key|Description|Type|Default Value|
Expand Down Expand Up @@ -172,6 +182,34 @@
|path|The path for the LevelDB persistence directory|`string`|`<nil>`
|syncWrites|Whether to synchronously perform writes to the storage|`boolean`|`false`

## persistence.postgres

|Key|Description|Type|Default Value|
|---|-----------|----|-------------|
|maxConnIdleTime|The maximum amount of time a database connection can be idle|[`time.Duration`](https://pkg.go.dev/time#Duration)|`1m`
|maxConnLifetime|The maximum amount of time to keep a database connection open|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
|maxConns|Maximum connections to the database|`int`|`50`
|maxIdleConns|The maximum number of idle connections to the database|`int`|`<nil>`
|url|The PostgreSQL connection string for the database|`string`|`<nil>`

## persistence.postgres.migrations

|Key|Description|Type|Default Value|
|---|-----------|----|-------------|
|auto|Enables automatic database migrations|`boolean`|`false`
|directory|The directory containing the numerically ordered migration DDL files to apply to the database|`string`|`./db/migrations/postgres`

## persistence.postgres.txwriter

|Key|Description|Type|Default Value|
|---|-----------|----|-------------|
|batchSize|Number of persistence operations on transactions to attempt to group into a DB transaction|`int`|`100`
|batchTimeout|Duration to hold batch open for new transaction operations before flushing to the DB|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10ms`
|cacheSlots|Number of transactions to hold cached metadata for to avoid DB read operations to calculate history|`int`|`1000`
|count|Number of transactions writing routines to start|`int`|`5`
|historyCompactionInterval|Duration between cleanup activities on the DB for a transaction with a large history|[`time.Duration`](https://pkg.go.dev/time#Duration)|`5m`
|historySummaryLimit|Maximum number of action entries to return embedded in the JSON response object when querying a transaction summary|`int`|`50`

## policyengine

|Key|Description|Type|Default Value|
Expand Down Expand Up @@ -271,7 +309,6 @@
|fixedGasPrice|A fixed gasPrice value/structure to pass to the connector|Raw JSON|`<nil>`
|interval|Interval at which to invoke the transaction handler loop to evaluate outstanding transactions|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
|maxInFlight|The maximum number of transactions to have in-flight with the transaction handler / blockchain transaction pool|`int`|`<nil>`
|nonceStateTimeout|How old the most recently submitted transaction record in our local state needs to be, before we make a request to the node to query the next nonce for a signing address|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
|resubmitInterval|The time between warning and re-sending a transaction (same nonce) when a blockchain transaction has not been allocated a receipt|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`

## transactions.handler.simple.gasOracle
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN;
DROP INDEX transactions_id;
DROP INDEX transactions_nonce;
DROP TABLE transactions;
COMMIT;
25 changes: 25 additions & 0 deletions db/migrations/postgres/000001_create_transactions_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BEGIN;
CREATE TABLE transactions (
seq SERIAL PRIMARY KEY,
id TEXT NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
status VARCHAR(65) NOT NULL,
delete BIGINT,
tx_from TEXT,
tx_to TEXT,
tx_nonce VARCHAR(65),
tx_gas VARCHAR(65),
tx_value VARCHAR(65),
tx_gasprice TEXT,
tx_data TEXT NOT NULL,
tx_hash TEXT NOT NULL,
policy_info TEXT,
first_submit BIGINT,
last_submit BIGINT,
error_message TEXT NOT NULL
);
CREATE UNIQUE INDEX transactions_id ON transactions(id);
CREATE UNIQUE INDEX transactions_nonce ON transactions(tx_from, tx_nonce);
CREATE INDEX transactions_hash ON transactions(tx_hash);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN;
DROP INDEX receipts_id;
DROP TABLE receipts;
COMMIT;
16 changes: 16 additions & 0 deletions db/migrations/postgres/000002_create_receipts_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BEGIN;
CREATE TABLE receipts (
seq SERIAL PRIMARY KEY,
id TEXT NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
block_number VARCHAR(65),
tx_index VARCHAR(65),
block_hash TEXT NOT NULL,
success BOOLEAN NOT NULL,
protocol_id TEXT NOT NULL,
extra_info TEXT,
contract_loc TEXT
);
CREATE UNIQUE INDEX receipts_id ON receipts(id);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN;
DROP INDEX confirmations_id;
DROP INDEX confirmations_txid;
DROP TABLE confirmations;
COMMIT;
14 changes: 14 additions & 0 deletions db/migrations/postgres/000003_create_confirmations_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
BEGIN;
CREATE TABLE confirmations (
seq SERIAL PRIMARY KEY,
id UUID NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
tx_id TEXT NOT NULL,
block_number BIGINT NOT NULL,
block_hash TEXT NOT NULL,
parent_hash TEXT NOT NULL
);
CREATE UNIQUE INDEX confirmations_id ON confirmations(id);
CREATE INDEX confirmations_txid ON confirmations(tx_id);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN;
DROP INDEX IF EXISTS txhistory_id;
DROP INDEX IF EXISTS txhistory_txid;
DROP TABLE txhistory;
COMMIT;
15 changes: 15 additions & 0 deletions db/migrations/postgres/000004_create_txhistory_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
BEGIN;
CREATE TABLE txhistory (
seq SERIAL PRIMARY KEY,
id UUID NOT NULL,
time BIGINT NOT NULL,
last_occurrence BIGINT NOT NULL,
tx_id TEXT NOT NULL,
status TEXT NOT NULL,
action TEXT NOT NULL,
count INT NOT NULL,
error TEXT,
error_time BIGINT,
info TEXT
);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN;
DROP INDEX checkpoints_id;
DROP TABLE checkpoints;
COMMIT;
10 changes: 10 additions & 0 deletions db/migrations/postgres/000005_create_checkpoints_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
BEGIN;
CREATE TABLE checkpoints (
seq SERIAL PRIMARY KEY,
id UUID NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
listeners JSON
);
CREATE UNIQUE INDEX checkpoints_id ON checkpoints(id);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN;
DROP INDEX eventstreams_id;
DROP INDEX eventstreams_name;
DROP TABLE eventstreams;
COMMIT;
20 changes: 20 additions & 0 deletions db/migrations/postgres/000006_create_eventstreams_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
BEGIN;
CREATE TABLE eventstreams (
seq SERIAL PRIMARY KEY,
id UUID NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
name TEXT,
suspended BOOLEAN,
stream_type TEXT,
error_handling TEXT,
batch_size BIGINT,
batch_timeout TEXT NOT NULL,
retry_timeout TEXT NOT NULL,
blocked_retry_timeout TEXT NOT NULL,
webhook_config TEXT,
websocket_config TEXT
);
CREATE UNIQUE INDEX eventstreams_id ON eventstreams(id);
CREATE UNIQUE INDEX eventstreams_name ON eventstreams(name);
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN;
DROP INDEX listeners_id;
DROP INDEX listeners_name;
DROP INDEX listeners_stream;
DROP TABLE listeners;
COMMIT;
17 changes: 17 additions & 0 deletions db/migrations/postgres/000007_create_listeners_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
BEGIN;
CREATE TABLE listeners (
seq SERIAL PRIMARY KEY,
id UUID NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
name TEXT,
stream_id UUID NOT NULL,
filters TEXT,
options TEXT,
signature TEXT,
from_block TEXT
);
CREATE UNIQUE INDEX listeners_id ON listeners(id);
CREATE UNIQUE INDEX listeners_name ON listeners(name); -- global uniqueness on names
CREATE INDEX listeners_stream ON listeners(stream_id);
COMMIT;
4 changes: 4 additions & 0 deletions db/migrations/postgres/000008_create_txhistory_idx.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN;
DROP INDEX IF EXISTS txhistory_id;
DROP INDEX IF EXISTS txhistory_txid;
COMMIT;
12 changes: 12 additions & 0 deletions db/migrations/postgres/000008_create_txhistory_idx.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BEGIN;
-- Allow nil data on transactions, for example for a simple transfer operation
ALTER TABLE transactions ALTER COLUMN tx_data DROP NOT NULL;

-- Correct TXHistory indexes if created by an invalid 000004 migration (no longer in codebase).
DROP INDEX IF EXISTS txhistory_id;
DROP INDEX IF EXISTS txhistory_txid;

-- Create corrected TXHistory indexes
CREATE UNIQUE INDEX txhistory_id ON txhistory(id);
CREATE INDEX txhistory_txid ON txhistory(tx_id);
COMMIT;
Loading