summaryrefslogtreecommitdiff
path: root/src/exchangedb
diff options
context:
space:
mode:
authorChristian Grothoff <grothoff@gnunet.org>2022-03-26 09:00:19 +0100
committerChristian Grothoff <grothoff@gnunet.org>2022-03-26 09:00:19 +0100
commit783e2ae424fdd338da142e2e7472ee86b27d4035 (patch)
tree19e064be30a2276f2e07988b199f3a0d95615e0e /src/exchangedb
parent238761c87d37dd03e47e79c368d2af4ea570158d (diff)
downloadexchange-783e2ae424fdd338da142e2e7472ee86b27d4035.tar.gz
exchange-783e2ae424fdd338da142e2e7472ee86b27d4035.tar.bz2
exchange-783e2ae424fdd338da142e2e7472ee86b27d4035.zip
add partitions to new p2p tables
Diffstat (limited to 'src/exchangedb')
-rw-r--r--src/exchangedb/exchange-0001.sql642
-rw-r--r--src/exchangedb/exchangedb.conf4
-rw-r--r--src/exchangedb/partition-0001.sql24
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c24
-rw-r--r--src/exchangedb/test-exchange-db-postgres.conf3
5 files changed, 497 insertions, 200 deletions
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index 568779f97..a42baa1f3 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -21,6 +21,8 @@ BEGIN;
SELECT _v.register_patch('exchange-0001', NULL, NULL);
+-- ------------------------------ denominations ----------------------------------------
+
CREATE TABLE IF NOT EXISTS denominations
(denominations_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
@@ -57,6 +59,8 @@ CREATE INDEX IF NOT EXISTS denominations_by_expire_legal_index
(expire_legal);
+-- ------------------------------ denomination_revocations ----------------------------------------
+
CREATE TABLE IF NOT EXISTS denomination_revocations
(denom_revocations_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,denominations_serial INT8 PRIMARY KEY REFERENCES denominations (denominations_serial) ON DELETE CASCADE
@@ -66,6 +70,8 @@ COMMENT ON TABLE denomination_revocations
IS 'remembering which denomination keys have been revoked';
+-- ------------------------------ wire_targets ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wire_targets
(wire_target_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,wire_target_h_payto BYTEA PRIMARY KEY CHECK (LENGTH(wire_target_h_payto)=32)
@@ -106,14 +112,13 @@ $$;
SELECT add_constraints_to_wire_targets_partition('default');
--- FIXME partition by serial_id rather than h_payto,
--- it is used more in join conditions - crucial for sharding to select this.
--- Author: (Boss Marco)
CREATE INDEX IF NOT EXISTS wire_targets_serial_id_index
ON wire_targets
(wire_target_serial_id);
+-- ------------------------------ reserves ----------------------------------------
+
CREATE TABLE IF NOT EXISTS reserves
(reserve_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY
,reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)
@@ -154,6 +159,7 @@ CREATE INDEX IF NOT EXISTS reserves_by_gc_date_index
COMMENT ON INDEX reserves_by_gc_date_index
IS 'for reserve garbage collection';
+-- ------------------------------ reserves_in ----------------------------------------
CREATE TABLE IF NOT EXISTS reserves_in
(reserve_in_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -199,18 +205,22 @@ SELECT add_constraints_to_reserves_in_partition('default');
CREATE INDEX IF NOT EXISTS reserves_in_by_reserve_in_serial_id_index
ON reserves_in
(reserve_in_serial_id);
+-- FIXME: where do we need this index? Can we do better?
CREATE INDEX IF NOT EXISTS reserves_in_by_exchange_account_section_execution_date_index
ON reserves_in
(exchange_account_section
,execution_date
);
+-- FIXME: where do we need this index? Can we do better?
CREATE INDEX IF NOT EXISTS reserves_in_by_exchange_account_reserve_in_serial_id_index
ON reserves_in
- (exchange_account_section,
- reserve_in_serial_id DESC
+ (exchange_account_section
+ ,reserve_in_serial_id DESC
);
+-- ------------------------------ reserves_close ----------------------------------------
+
CREATE TABLE IF NOT EXISTS reserves_close
(close_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE / PRIMARY KEY
,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE
@@ -256,6 +266,7 @@ $$;
SELECT add_constraints_to_reserves_close_partition('default');
+-- ------------------------------ reserves_out ----------------------------------------
CREATE TABLE IF NOT EXISTS reserves_out
(reserve_out_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -279,14 +290,13 @@ COMMENT ON COLUMN reserves_out.denominations_serial
CREATE INDEX IF NOT EXISTS reserves_out_by_reserve_out_serial_id_index
ON reserves_out
(reserve_out_serial_id);
+-- FIXME: change query to use reserves_out_by_reserve instead and materialize execution_date there as well???
CREATE INDEX IF NOT EXISTS reserves_out_by_reserve_uuid_and_execution_date_index
ON reserves_out
(reserve_uuid, execution_date);
COMMENT ON INDEX reserves_out_by_reserve_uuid_and_execution_date_index
IS 'for get_reserves_out and exchange_do_withdraw_limit_check';
-
-
CREATE TABLE IF NOT EXISTS reserves_out_default
PARTITION OF reserves_out
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
@@ -309,6 +319,63 @@ $$;
SELECT add_constraints_to_reserves_out_partition('default');
+CREATE TABLE IF NOT EXISTS reserves_out_by_reserve
+ (reserve_uuid INT8 NOT NULL -- REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
+ ,h_blind_ev BYTEA CHECK (LENGTH(h_blind_ev)=64)
+ )
+ PARTITION BY HASH (reserve_uuid);
+COMMENT ON TABLE reserves_out_by_reserve
+ IS 'Information in this table is strictly redundant with that of reserves_out, but saved by a different primary key for fast lookups by reserve public key/uuid.';
+
+CREATE INDEX IF NOT EXISTS reserves_out_by_reserve_main_index
+ ON reserves_out_by_reserve
+ (reserve_uuid);
+
+CREATE TABLE IF NOT EXISTS reserves_out_by_reserve_default
+ PARTITION OF reserves_out_by_reserve
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION reserves_out_by_reserve_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserves_out_by_reserve
+ (reserve_uuid
+ ,h_blind_ev)
+ VALUES
+ (NEW.reserve_uuid
+ ,NEW.h_blind_ev);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION reserves_out_by_reserve_insert_trigger()
+ IS 'Replicate reserve_out inserts into reserve_out_by_reserve table.';
+
+CREATE TRIGGER reserves_out_on_insert
+ AFTER INSERT
+ ON reserves_out
+ FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_insert_trigger();
+
+CREATE OR REPLACE FUNCTION reserves_out_by_reserve_delete_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ DELETE FROM reserves_out_by_reserve
+ WHERE reserve_uuid = OLD.reserve_uuid;
+ RETURN OLD;
+END $$;
+COMMENT ON FUNCTION reserves_out_by_reserve_delete_trigger()
+ IS 'Replicate reserve_out deletions into reserve_out_by_reserve table.';
+
+CREATE TRIGGER reserves_out_on_delete
+ AFTER DELETE
+ ON reserves_out
+ FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_delete_trigger();
+
+
+-- ------------------------------ auditors ----------------------------------------
+
CREATE TABLE IF NOT EXISTS auditors
(auditor_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,auditor_pub BYTEA PRIMARY KEY CHECK (LENGTH(auditor_pub)=32)
@@ -329,6 +396,8 @@ COMMENT ON COLUMN auditors.last_change
IS 'Latest time when active status changed. Used to detect replays of old messages.';
+-- ------------------------------ auditor_denom_sigs ----------------------------------------
+
CREATE TABLE IF NOT EXISTS auditor_denom_sigs
(auditor_denom_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,auditor_uuid INT8 NOT NULL REFERENCES auditors (auditor_uuid) ON DELETE CASCADE
@@ -346,6 +415,8 @@ COMMENT ON COLUMN auditor_denom_sigs.auditor_sig
IS 'Signature of the auditor, of purpose TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS.';
+-- ------------------------------ exchange_sign_keys ----------------------------------------
+
CREATE TABLE IF NOT EXISTS exchange_sign_keys
(esk_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,exchange_pub BYTEA PRIMARY KEY CHECK (LENGTH(exchange_pub)=32)
@@ -368,6 +439,8 @@ COMMENT ON COLUMN exchange_sign_keys.expire_legal
IS 'Time when this online signing key legally expires.';
+-- ------------------------------ signkey_revocations ----------------------------------------
+
CREATE TABLE IF NOT EXISTS signkey_revocations
(signkey_revocations_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,esk_serial INT8 PRIMARY KEY REFERENCES exchange_sign_keys (esk_serial) ON DELETE CASCADE
@@ -377,6 +450,8 @@ COMMENT ON TABLE signkey_revocations
IS 'Table storing which online signing keys have been revoked';
+-- ------------------------------ extension ----------------------------------------
+
CREATE TABLE IF NOT EXISTS extensions
(extension_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,name VARCHAR NOT NULL UNIQUE
@@ -390,6 +465,8 @@ COMMENT ON COLUMN extensions.config
IS 'Configuration of the extension as JSON-blob, maybe NULL';
+-- ------------------------------ known_coins ----------------------------------------
+
CREATE TABLE IF NOT EXISTS known_coins
(known_coin_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial) ON DELETE CASCADE
@@ -399,7 +476,7 @@ CREATE TABLE IF NOT EXISTS known_coins
,remaining_val INT8 NOT NULL
,remaining_frac INT4 NOT NULL
)
- PARTITION BY HASH (coin_pub); -- FIXME: or include denominations_serial? or multi-level partitioning?
+ PARTITION BY HASH (coin_pub);
COMMENT ON TABLE known_coins
IS 'information about coins and their signatures, so we do not have to store the signatures more than once if a coin is involved in multiple operations';
COMMENT ON COLUMN known_coins.denominations_serial
@@ -434,10 +511,8 @@ $$;
SELECT add_constraints_to_known_coins_partition('default');
-CREATE INDEX IF NOT EXISTS known_coins_by_known_coin_id_index
- ON known_coins
- (known_coin_id);
+-- ------------------------------ refresh_commitments ----------------------------------------
CREATE TABLE IF NOT EXISTS refresh_commitments
(melt_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -458,6 +533,11 @@ COMMENT ON COLUMN refresh_commitments.rc
COMMENT ON COLUMN refresh_commitments.old_coin_pub
IS 'Coin being melted in the refresh process.';
+-- Note: index spans partitions, may need to be materialized.
+CREATE INDEX IF NOT EXISTS refresh_commitments_by_old_coin_pub_index
+ ON refresh_commitments
+ (old_coin_pub);
+
CREATE TABLE IF NOT EXISTS refresh_commitments_default
PARTITION OF refresh_commitments
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
@@ -479,13 +559,8 @@ $$;
SELECT add_constraints_to_refresh_commitments_partition('default');
-CREATE INDEX IF NOT EXISTS refresh_commitments_by_melt_serial_id_index
- ON refresh_commitments
- (melt_serial_id);
-CREATE INDEX IF NOT EXISTS refresh_commitments_by_old_coin_pub_index
- ON refresh_commitments
- (old_coin_pub);
+-- ------------------------------ refresh_revealed_coins ----------------------------------------
CREATE TABLE IF NOT EXISTS refresh_revealed_coins
(rrc_serial BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -517,9 +592,6 @@ COMMENT ON COLUMN refresh_revealed_coins.h_coin_ev
COMMENT ON COLUMN refresh_revealed_coins.ev_sig
IS 'exchange signature over the envelope';
-CREATE INDEX IF NOT EXISTS refresh_revealed_coins_by_rrc_serial_index
- ON refresh_revealed_coins
- (rrc_serial);
CREATE INDEX IF NOT EXISTS refresh_revealed_coins_by_melt_serial_id_index
ON refresh_revealed_coins
(melt_serial_id);
@@ -551,7 +623,7 @@ $$;
SELECT add_constraints_to_refresh_revealed_coins_partition('default');
-
+-- ------------------------------ refresh_transfer_keys ----------------------------------------
CREATE TABLE IF NOT EXISTS refresh_transfer_keys
(rtc_serial BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -592,25 +664,30 @@ $$;
SELECT add_constraints_to_refresh_transfer_keys_partition('default');
-CREATE INDEX IF NOT EXISTS refresh_transfer_keys_by_rtc_serial_index
- ON refresh_transfer_keys
- (rtc_serial);
+-- ------------------------------ extension_details ----------------------------------------
CREATE TABLE IF NOT EXISTS extension_details
(extension_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
- ,extension_options VARCHAR);
+ ,extension_options VARCHAR)
+ PARTITION BY HASH (extension_details_serial_id);
COMMENT ON TABLE extension_details
IS 'Extensions that were provided with deposits (not yet used).';
COMMENT ON COLUMN extension_details.extension_options
IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the extensions supported by the exchange.';
+CREATE TABLE IF NOT EXISTS extension_details_default
+ PARTITION OF extension_details
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+
+-- ------------------------------ deposits ----------------------------------------
CREATE TABLE IF NOT EXISTS deposits
(deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- PRIMARY KEY
,shard INT8 NOT NULL
,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32) -- REFERENCES known_coins (coin_pub) ON DELETE CASCADE
- ,known_coin_id BIGINT NOT NULL -- REFERENCES known_coins (known_coin_id) ON DELETE CASCADE --- FIXME: column needed???
+ ,known_coin_id BIGINT NOT NULL -- REFERENCES known_coins (known_coin_id) ON DELETE CASCADE
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,wallet_timestamp INT8 NOT NULL
@@ -629,34 +706,10 @@ CREATE TABLE IF NOT EXISTS deposits
,UNIQUE (coin_pub, merchant_pub, h_contract_terms)
)
PARTITION BY HASH (coin_pub);
--- FIXME:
--- TODO: dynamically (!) creating/deleting partitions:
--- create new partitions 'as needed', drop old ones once the aggregator has made
--- them empty; as 'new' deposits will always have deadlines in the future, this
--- would basically guarantee no conflict between aggregator and exchange service!
--- SEE also: https://www.cybertec-postgresql.com/en/automatic-partition-creation-in-postgresql/
--- (article is slightly wrong, as this works:)
---CREATE TABLE tab (
--- id bigint GENERATED ALWAYS AS IDENTITY,
--- ts timestamp NOT NULL,
--- data text
--- PARTITION BY LIST ((ts::date));
--- CREATE TABLE tab_def PARTITION OF tab DEFAULT;
--- BEGIN
--- CREATE TABLE tab_part2 (LIKE tab);
--- insert into tab_part2 (id,ts, data) values (5,'2022-03-21', 'foo');
--- alter table tab attach partition tab_part2 for values in ('2022-03-21');
--- commit;
--- Naturally, to ensure this is actually 100% conflict-free, we'd
--- need to create tables at the granularity of the wire/refund deadlines;
--- that is right now seconds (!). But I see no problem with changing the
--- aggregator to basically always run 1 minute behind and use minutes instead!
-
-
COMMENT ON TABLE deposits
IS 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).';
COMMENT ON COLUMN deposits.shard
- IS 'Used for load sharding. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.';
+ IS 'Used for load sharding in the materialized indices. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.';
COMMENT ON COLUMN deposits.known_coin_id
IS 'Used for garbage collection';
COMMENT ON COLUMN deposits.wire_target_h_payto
@@ -672,17 +725,10 @@ COMMENT ON COLUMN deposits.extension_details_serial_id
COMMENT ON COLUMN deposits.tiny
IS 'Set to TRUE if we decided that the amount is too small to ever trigger a wire transfer by itself (requires real aggregation)';
--- FIXME: we sometimes go ONLY by 'deposit_serial_id',
--- check if queries could be improved by adding shard or adding another index without shard here, or inverting the order of the index here!
-CREATE INDEX IF NOT EXISTS deposits_deposit_by_serial_id_index
- ON deposits
- (shard,deposit_serial_id);
-
CREATE INDEX IF NOT EXISTS deposits_by_coin_pub_index
ON deposits
(coin_pub);
-
CREATE TABLE IF NOT EXISTS deposits_default
PARTITION OF deposits
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
@@ -889,7 +935,7 @@ BEGIN
AND deposit_serial_id = OLD.deposit_serial_id;
END IF;
RETURN NEW;
-END $$;
+END $$;
COMMENT ON FUNCTION deposits_delete_trigger()
IS 'Replicate deposit deletions into materialized indices.';
@@ -899,9 +945,13 @@ CREATE TRIGGER deposits_on_delete
FOR EACH ROW EXECUTE FUNCTION deposits_delete_trigger();
+-- ------------------------------ refunds ----------------------------------------
+
+-- FIXME-URGENT: very bad structure, should replace 'shard' by 'coin_pub'
+-- as deposits is sharded by that now!
CREATE TABLE IF NOT EXISTS refunds
(refund_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
- ,shard INT8 NOT NULL -- REFERENCES deposits (shard)
+ ,shard INT8 NOT NULL -- REFERENCES deposits (shard)
,deposit_serial_id INT8 NOT NULL -- REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE
,merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)
,rtransaction_id INT8 NOT NULL
@@ -939,14 +989,12 @@ $$;
SELECT add_constraints_to_refunds_partition('default');
-CREATE INDEX IF NOT EXISTS refunds_by_refund_serial_id_index
- ON refunds
- (refund_serial_id);
CREATE INDEX IF NOT EXISTS refunds_by_deposit_serial_id_index
ON refunds
(shard,deposit_serial_id);
+-- ------------------------------ wire_out ----------------------------------------
CREATE TABLE IF NOT EXISTS wire_out
(wireout_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY -- PRIMARY KEY
@@ -972,7 +1020,6 @@ CREATE INDEX IF NOT EXISTS wire_out_by_wire_target_h_payto_index
ON wire_out
(wire_target_h_payto);
-
CREATE TABLE IF NOT EXISTS wire_out_default
PARTITION OF wire_out
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
@@ -995,10 +1042,13 @@ $$;
SELECT add_constraints_to_wire_out_partition('default');
+-- ------------------------------ aggregation_tracking ----------------------------------------
+-- FIXME-URGENT: add colum coin_pub to select by coin_pub + deposit_serial_id for more efficient deposit lookup!?
+-- Or which direction(s) is this table used? Is the partitioning sane??
CREATE TABLE IF NOT EXISTS aggregation_tracking
(aggregation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
- ,deposit_serial_id INT8 PRIMARY KEY -- REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE -- FIXME: change to coin_pub + deposit_serial_id for more efficient deposit -- or something else???
+ ,deposit_serial_id INT8 PRIMARY KEY -- REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE
,wtid_raw BYTEA NOT NULL CONSTRAINT wire_out_ref REFERENCES wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE
)
PARTITION BY HASH (deposit_serial_id);
@@ -1028,9 +1078,6 @@ $$;
SELECT add_constraints_to_aggregation_tracking_partition('default');
-CREATE INDEX IF NOT EXISTS aggregation_tracking_by_aggregation_serial_id_index
- ON aggregation_tracking
- (aggregation_serial_id);
CREATE INDEX IF NOT EXISTS aggregation_tracking_by_wtid_raw_index
ON aggregation_tracking
(wtid_raw);
@@ -1038,6 +1085,8 @@ COMMENT ON INDEX aggregation_tracking_by_wtid_raw_index
IS 'for lookup_transactions';
+-- ------------------------------ wire_fee ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wire_fee
(wire_fee_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,wire_method VARCHAR NOT NULL
@@ -1062,6 +1111,8 @@ CREATE INDEX IF NOT EXISTS wire_fee_by_end_date_index
(end_date);
+-- ------------------------------ global_fee ----------------------------------------
+
CREATE TABLE IF NOT EXISTS global_fee
(global_fee_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,start_date INT8 NOT NULL
@@ -1091,6 +1142,8 @@ CREATE INDEX IF NOT EXISTS global_fee_by_end_date_index
(end_date);
+-- ------------------------------ recoup ----------------------------------------
+
CREATE TABLE IF NOT EXISTS recoup
(recoup_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32) -- REFERENCES known_coins (coin_pub)
@@ -1113,17 +1166,10 @@ COMMENT ON COLUMN recoup.coin_sig
COMMENT ON COLUMN recoup.coin_blind
IS 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the withdraw operation.';
-CREATE INDEX IF NOT EXISTS recoup_by_recoup_uuid_index
- ON recoup
- (recoup_uuid);
-CREATE INDEX IF NOT EXISTS recoup_by_reserve_out_serial_id_index
- ON recoup
- (reserve_out_serial_id);
CREATE INDEX IF NOT EXISTS recoup_by_coin_pub_index
ON recoup
(coin_pub);
-
CREATE TABLE IF NOT EXISTS recoup_default
PARTITION OF recoup
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
@@ -1145,7 +1191,6 @@ $$;
SELECT add_constraints_to_recoup_partition('default');
-
CREATE TABLE IF NOT EXISTS recoup_by_reserve
(reserve_out_serial_id INT8 NOT NULL -- REFERENCES reserves (reserve_out_serial_id) ON DELETE CASCADE
,coin_pub BYTEA CHECK (LENGTH(coin_pub)=32)
@@ -1183,7 +1228,6 @@ CREATE TRIGGER recoup_on_insert
ON recoup
FOR EACH ROW EXECUTE FUNCTION recoup_insert_trigger();
-
CREATE OR REPLACE FUNCTION recoup_delete_trigger()
RETURNS trigger
LANGUAGE plpgsql
@@ -1203,68 +1247,7 @@ CREATE TRIGGER recoup_on_delete
FOR EACH ROW EXECUTE FUNCTION recoup_delete_trigger();
-
-
-
-CREATE TABLE IF NOT EXISTS reserves_out_by_reserve
- (reserve_uuid INT8 NOT NULL -- REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
- ,h_blind_ev BYTEA CHECK (LENGTH(h_blind_ev)=64)
- )
- PARTITION BY HASH (reserve_uuid);
-COMMENT ON TABLE reserves_out_by_reserve
- IS 'Information in this table is strictly redundant with that of reserves_out, but saved by a different primary key for fast lookups by reserve public key/uuid.';
-
-CREATE INDEX IF NOT EXISTS reserves_out_by_reserve_main_index
- ON reserves_out_by_reserve
- (reserve_uuid);
-
-
-CREATE TABLE IF NOT EXISTS reserves_out_by_reserve_default
- PARTITION OF reserves_out_by_reserve
- FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-
-CREATE OR REPLACE FUNCTION reserves_out_by_reserve_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO reserves_out_by_reserve
- (reserve_uuid
- ,h_blind_ev)
- VALUES
- (NEW.reserve_uuid
- ,NEW.h_blind_ev);
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION reserves_out_by_reserve_insert_trigger()
- IS 'Replicate reserve_out inserts into reserve_out_by_reserve table.';
-
-CREATE TRIGGER reserves_out_on_insert
- AFTER INSERT
- ON reserves_out
- FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_insert_trigger();
-
-
-CREATE OR REPLACE FUNCTION reserves_out_by_reserve_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- DELETE FROM reserves_out_by_reserve
- WHERE reserve_uuid = OLD.reserve_uuid;
- RETURN OLD;
-END $$;
-COMMENT ON FUNCTION reserves_out_by_reserve_delete_trigger()
- IS 'Replicate reserve_out deletions into reserve_out_by_reserve table.';
-
-CREATE TRIGGER reserves_out_on_delete
- AFTER DELETE
- ON reserves_out
- FOR EACH ROW EXECUTE FUNCTION reserves_out_by_reserve_delete_trigger();
-
-
-
-
+-- ------------------------------ recoup_refresh ----------------------------------------
CREATE TABLE IF NOT EXISTS recoup_refresh
(recoup_refresh_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
@@ -1289,16 +1272,14 @@ COMMENT ON COLUMN recoup_refresh.rrc_serial
COMMENT ON COLUMN recoup_refresh.coin_blind
IS 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the refresh operation.';
-CREATE INDEX IF NOT EXISTS recoup_refresh_by_recoup_refresh_uuid_index
+CREATE INDEX IF NOT EXISTS recoup_refresh_by_coin_pub_index
ON recoup_refresh
- (recoup_refresh_uuid);
+ (coin_pub);
+-- FIXME: any query using this index will be slow. Materialize index or change query?
+-- Also: which query uses this index?
CREATE INDEX IF NOT EXISTS recoup_refresh_by_rrc_serial_index
ON recoup_refresh
(rrc_serial);
-CREATE INDEX IF NOT EXISTS recoup_refresh_by_coin_pub_index
- ON recoup_refresh
- (coin_pub);
-
CREATE TABLE IF NOT EXISTS recoup_refresh_default
PARTITION OF recoup_refresh
@@ -1322,6 +1303,7 @@ $$;
SELECT add_constraints_to_recoup_refresh_partition('default');
+-- ------------------------------ prewire ----------------------------------------
CREATE TABLE IF NOT EXISTS prewire
(prewire_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
@@ -1357,6 +1339,7 @@ CREATE TABLE IF NOT EXISTS prewire_default
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+-- ------------------------------ wire_accounts ----------------------------------------
CREATE TABLE IF NOT EXISTS wire_accounts
(payto_uri VARCHAR PRIMARY KEY
@@ -1378,6 +1361,8 @@ COMMENT ON COLUMN wire_accounts.last_change
-- and is of no concern to the auditor
+-- ------------------------------ cs_nonce_locks ----------------------------------------
+
CREATE TABLE IF NOT EXISTS cs_nonce_locks
(cs_nonce_lock_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,nonce BYTEA PRIMARY KEY CHECK (LENGTH(nonce)=32)
@@ -1415,6 +1400,9 @@ $$;
SELECT add_constraints_to_cs_nonce_locks_partition('default');
+
+-- ------------------------------ work_shards ----------------------------------------
+
CREATE TABLE IF NOT EXISTS work_shards
(shard_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,last_attempt INT8 NOT NULL
@@ -1447,6 +1435,8 @@ CREATE INDEX IF NOT EXISTS work_shards_by_job_name_completed_last_attempt_index
);
+-- ------------------------------ revolving_work_shards ----------------------------------------
+
CREATE UNLOGGED TABLE IF NOT EXISTS revolving_work_shards
(shard_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,last_attempt INT8 NOT NULL
@@ -1478,10 +1468,14 @@ CREATE INDEX IF NOT EXISTS revolving_work_shards_by_job_name_active_last_attempt
,last_attempt
);
--- Tables for P2P payments
+--------------------------------------------------------------------------
+-- Tables for P2P payments
+--------------------------------------------------------------------------
+
+-- ------------------------------ partners ----------------------------------------
CREATE TABLE IF NOT EXISTS partners
- (partner_serial_id BIGSERIAL UNIQUE
+ (partner_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,partner_master_pub BYTEA NOT NULL CHECK(LENGTH(partner_master_pub)=32)
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
@@ -1509,8 +1503,10 @@ COMMENT ON COLUMN partners.master_sig
IS 'signature of our master public key affirming the partnership, of purpose TALER_SIGNATURE_MASTER_PARTNER_DETAILS';
+-- ------------------------------ purse_requests ----------------------------------------
+
CREATE TABLE IF NOT EXISTS purse_requests
- (purse_deposit_serial_id BIGSERIAL UNIQUE
+ (purse_requests_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)
,merge_pub BYTEA NOT NULL CHECK (LENGTH(merge_pub)=32)
,purse_expiration INT8 NOT NULL
@@ -1522,7 +1518,8 @@ CREATE TABLE IF NOT EXISTS purse_requests
,balance_frac INT4 NOT NULL DEFAULT (0)
,purse_sig BYTEA NOT NULL CHECK(LENGTH(purse_sig)=64)
,PRIMARY KEY (purse_pub)
- ); -- partition by purse_pub
+ )
+ PARTITION BY HASH (purse_pub);
COMMENT ON TABLE purse_requests
IS 'Requests establishing purses, associating them with a contract but without a target reserve';
COMMENT ON COLUMN purse_requests.purse_pub
@@ -1538,19 +1535,45 @@ COMMENT ON COLUMN purse_requests.balance_val
COMMENT ON COLUMN purse_requests.purse_sig
IS 'Signature of the purse affirming the purse parameters, of type TALER_SIGNATURE_PURSE_REQUEST';
--- FIXME: create purse_by_merge materialized index table
--- for merge_pub => purse_pub mapping!
+-- FIXME: change to materialized index by marge_pub!
+CREATE INDEX IF NOT EXISTS purse_requests_merge_pub
+ ON purse_requests (merge_pub);
+CREATE TABLE IF NOT EXISTS purse_requests_default
+ PARTITION OF purse_requests
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_purse_requests_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE purse_requests_' || partition_suffix || ' '
+ 'ADD CONSTRAINT purse_requests_' || partition_suffix || '_purse_requests_serial_id_key '
+ 'UNIQUE (purse_requests_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_purse_requests_partition('default');
+
+
+
+-- ------------------------------ purse_merges ----------------------------------------
CREATE TABLE IF NOT EXISTS purse_merges
- (purse_merge_request_serial_id BIGSERIAL -- UNIQUE
+ (purse_merge_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE
,reserve_pub BYTEA NOT NULL CHECK(length(reserve_pub)=32)--REFERENCES reserves (reserve_pub) ON DELETE CASCADE
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32) --REFERENCES purse_requests (purse_pub) ON DELETE CASCADE
,merge_sig BYTEA NOT NULL CHECK (LENGTH(merge_sig)=64)
,merge_timestamp INT8 NOT NULL
,PRIMARY KEY (purse_pub)
- ); -- partition by purse_pub; plus materialized index by reserve_pub!
+ )
+ PARTITION BY HASH (purse_pub);
COMMENT ON TABLE purse_merges
IS 'Merge requests where a purse-owner requested merging the purse into the account';
COMMENT ON COLUMN purse_merges.partner_serial_id
@@ -1563,42 +1586,99 @@ COMMENT ON COLUMN purse_merges.merge_sig
IS 'signature by the purse private key affirming the merge, of type TALER_SIGNATURE_WALLET_PURSE_MERGE';
COMMENT ON COLUMN purse_merges.merge_timestamp
IS 'when was the merge message signed';
+
+CREATE INDEX IF NOT EXISTS purse_merges_purse_pub
+ ON purse_merges (purse_pub);
+-- FIXME: change to materialized index by reserve_pub!
CREATE INDEX IF NOT EXISTS purse_merges_reserve_pub
ON purse_merges (reserve_pub);
COMMENT ON INDEX purse_merges_reserve_pub
IS 'needed in reserve history computation';
+CREATE TABLE IF NOT EXISTS purse_merges_default
+ PARTITION OF purse_merges
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-CREATE TABLE IF NOT EXISTS account_mergers
- (account_merge_request_serial_id BIGSERIAL -- UNIQUE
+CREATE OR REPLACE FUNCTION add_constraints_to_purse_merges_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE purse_merges_' || partition_suffix || ' '
+ 'ADD CONSTRAINT purse_merges_' || partition_suffix || '_purse_merge_request_serial_id_key '
+ 'UNIQUE (purse_merge_request_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_purse_merges_partition('default');
+
+
+
+-- ------------------------------ account_merges ----------------------------------------
+
+CREATE TABLE IF NOT EXISTS account_merges
+ (account_merge_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32) -- REFERENCES reserves (reserve_pub) ON DELETE CASCADE
,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32) -- REFERENCES purse_requests (purse_pub)
- ,PRIMARY KEY (reserve_pub)
- ); -- partition by purse_pub; plus materialized index by reserve_pub!
-COMMENT ON TABLE account_mergers
+ ,PRIMARY KEY (purse_pub)
+ )
+ PARTITION BY HASH (purse_pub);
+COMMENT ON TABLE account_merges
IS 'Merge requests where a purse- and account-owner requested merging the purse into the account';
-COMMENT ON COLUMN account_mergers.reserve_pub
+COMMENT ON COLUMN account_merges.reserve_pub
IS 'public key of the target reserve';
-COMMENT ON COLUMN account_mergers.purse_pub
+COMMENT ON COLUMN account_merges.purse_pub
IS 'public key of the purse';
-COMMENT ON COLUMN account_mergers.reserve_sig
+COMMENT ON COLUMN account_merges.reserve_sig
IS 'signature by the reserve private key affirming the merge, of type TALER_SIGNATURE_WALLET_ACCOUNT_MERGE';
-CREATE INDEX IF NOT EXISTS account_mergers_purse_pub
- ON account_mergers (purse_pub);
-COMMENT ON INDEX account_mergers_purse_pub
+CREATE INDEX IF NOT EXISTS account_merges_purse_pub
+ ON account_merges (purse_pub);
+COMMENT ON INDEX account_merges_purse_pub
IS 'needed when checking for a purse merge status';
-
+
+-- FIXME: change to materialized index by reserve_pub!
+CREATE INDEX IF NOT EXISTS account_merges_by_reserve_pub
+ ON account_merges (reserve_pub);
+
+CREATE TABLE IF NOT EXISTS account_merges_default
+ PARTITION OF account_merges
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_account_merges_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE account_merges_' || partition_suffix || ' '
+ 'ADD CONSTRAINT account_merges_' || partition_suffix || '_account_merge_request_serial_id_key '
+ 'UNIQUE (account_merge_request_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_account_merges_partition('default');
+
+
+-- ------------------------------ contracts ----------------------------------------
CREATE TABLE IF NOT EXISTS contracts
- (contract_serial_id BIGSERIAL UNIQUE
+ (contract_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)
,pub_ckey BYTEA NOT NULL CHECK (LENGTH(pub_ckey)=32)
,e_contract BYTEA NOT NULL
,purse_expiration INT8 NOT NULL
,PRIMARY KEY (purse_pub)
- ); -- partition by purse_pub
+ )
+ PARTITION BY HASH (purse_pub);
COMMENT ON TABLE contracts
IS 'encrypted contracts associated with purses';
COMMENT ON COLUMN contracts.purse_pub
@@ -1608,6 +1688,30 @@ COMMENT ON COLUMN contracts.pub_ckey
COMMENT ON COLUMN contracts.e_contract
IS 'AES-GCM encrypted contract terms (contains gzip compressed JSON after decryption)';
+CREATE TABLE IF NOT EXISTS contracts_default
+ PARTITION OF contracts
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_contracts_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE contracts_' || partition_suffix || ' '
+ 'ADD CONSTRAINT contracts_' || partition_suffix || '_contract_serial_id_key '
+ 'UNIQUE (contract_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_contracts_partition('default');
+
+
+-- ------------------------------ history_requests ----------------------------------------
+
CREATE TABLE IF NOT EXISTS history_requests
(reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32) REFERENCES reserves(reserve_pub) ON DELETE CASCADE
,request_timestamp INT8 NOT NULL
@@ -1615,7 +1719,8 @@ CREATE TABLE IF NOT EXISTS history_requests
,history_fee_val INT8 NOT NULL
,history_fee_frac INT4 NOT NULL
,PRIMARY KEY (reserve_pub,request_timestamp)
- ); -- partition by reserve_pub
+ )
+ PARTITION BY HASH (reserve_pub);
COMMENT ON TABLE history_requests
IS 'Paid history requests issued by a client against a reserve';
COMMENT ON COLUMN history_requests.request_timestamp
@@ -1625,6 +1730,13 @@ COMMENT ON COLUMN history_requests.reserve_sig
COMMENT ON COLUMN history_requests.history_fee_val
IS 'History fee approved by the signature';
+CREATE TABLE IF NOT EXISTS history_requests_default
+ PARTITION OF history_requests
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+
+-- ------------------------------ close_requests ----------------------------------------
+
CREATE TABLE IF NOT EXISTS close_requests
(reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32) REFERENCES reserves(reserve_pub) ON DELETE CASCADE
,close_timestamp INT8 NOT NULL
@@ -1632,7 +1744,8 @@ CREATE TABLE IF NOT EXISTS close_requests
,close_val INT8 NOT NULL
,close_frac INT4 NOT NULL
,PRIMARY KEY (reserve_pub,close_timestamp)
- ); -- partition by reserve_pub
+ )
+ PARTITION BY HASH (reserve_pub);
COMMENT ON TABLE close_requests
IS 'Explicit requests by a reserve owner to close a reserve immediately';
COMMENT ON COLUMN close_requests.close_timestamp
@@ -1642,17 +1755,24 @@ COMMENT ON COLUMN close_requests.reserve_sig
COMMENT ON COLUMN close_requests.close_val
IS 'Balance of the reserve at the time of closing, to be wired to the associated bank account (minus the closing fee)';
+CREATE TABLE IF NOT EXISTS close_requests_default
+ PARTITION OF close_requests
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+
+-- ------------------------------ purse_deposits ----------------------------------------
CREATE TABLE IF NOT EXISTS purse_deposits
- (purse_deposit_serial_id BIGSERIAL UNIQUE
+ (purse_deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE
,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)
,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)
- ,PRIMARY KEY (purse_pub,coin_pub)
- ); -- partition by purse_pub, plus a materialized index by coin_pub!
+ -- ,PRIMARY KEY (purse_pub,coin_pub)
+ )
+ PARTITION BY HASH (purse_pub);
COMMENT ON TABLE purse_deposits
IS 'Requests depositing coins into a purse';
COMMENT ON COLUMN purse_deposits.partner_serial_id
@@ -1666,14 +1786,43 @@ COMMENT ON COLUMN purse_deposits.amount_with_fee_val
COMMENT ON COLUMN purse_deposits.coin_sig
IS 'Signature of the coin affirming the deposit into the purse, of type TALER_SIGNATURE_PURSE_DEPOSIT';
+-- FIXME: change to materialized index by coin_pub!
+CREATE INDEX IF NOT EXISTS purse_deposits_by_coin_pub
+ ON purse_deposits (coin_pub);
+
+CREATE TABLE IF NOT EXISTS purse_deposits_default
+ PARTITION OF purse_deposits
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_purse_deposits_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE purse_deposits_' || partition_suffix || ' '
+ 'ADD CONSTRAINT purse_deposits_' || partition_suffix || '_purse_deposit_serial_id_key '
+ 'UNIQUE (purse_deposit_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_purse_deposits_partition('default');
+
+
+-- ------------------------------ wads_out ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wads_out
- (wad_out_serial_id BIGSERIAL UNIQUE
+ (wad_out_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24)
,partner_serial_id INT8 NOT NULL REFERENCES partners(partner_serial_id) ON DELETE CASCADE
,amount_val INT8 NOT NULL
,amount_frac INT4 NOT NULL
,execution_time INT8 NOT NULL
- ); -- partition by wad_id
+ )
+ PARTITION BY HASH (wad_id);
COMMENT ON TABLE wads_out
IS 'Wire transfers made to another exchange to transfer purse funds';
COMMENT ON COLUMN wads_out.wad_id
@@ -1685,9 +1834,36 @@ COMMENT ON COLUMN wads_out.amount_val
COMMENT ON COLUMN wads_out.execution_time
IS 'Time when the wire transfer was scheduled';
+CREATE INDEX IF NOT EXISTS wads_out_index_by_wad_id
+ ON wads_out (wad_id);
+
+CREATE TABLE IF NOT EXISTS wads_out_default
+ PARTITION OF wads_out
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_wads_out_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE wads_out_' || partition_suffix || ' '
+ 'ADD CONSTRAINT wads_out_' || partition_suffix || '_wad_out_serial_id_key '
+ 'UNIQUE (wad_out_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_wads_out_partition('default');
+
+
+-- ------------------------------ wads_out_entries ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wad_out_entries
- (wad_out_entry_serial_id BIGSERIAL UNIQUE
- ,wad_out_serial_id INT8 REFERENCES wads_out (wad_out_serial_id) ON DELETE CASCADE
+ (wad_out_entry_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
+ ,wad_out_serial_id INT8 -- REFERENCES wads_out (wad_out_serial_id) ON DELETE CASCADE
,reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32)
,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32)
,h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64)
@@ -1701,9 +1877,11 @@ CREATE TABLE IF NOT EXISTS wad_out_entries
,deposit_fees_frac INT4 NOT NULL
,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)
,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)
- ); -- partition by purse_pub? do we need a materialized index by reserve_pub?
-CREATE INDEX IF NOT EXISTS wad_out_entries_index_by_wad
- ON wad_out_entries (wad_out_serial_id);
+ )
+ PARTITION BY HASH (purse_pub);
+-- FIXME: convert to materialized index!
+CREATE INDEX IF NOT EXISTS wad_out_entries_index_by_reserve_pub
+ ON wad_out_entries (reserve_pub);
COMMENT ON TABLE wad_out_entries
IS 'Purses combined into a wad';
COMMENT ON COLUMN wad_out_entries.wad_out_serial_id
@@ -1729,15 +1907,40 @@ COMMENT ON COLUMN wad_out_entries.reserve_sig
COMMENT ON COLUMN wad_out_entries.purse_sig
IS 'Signature by the purse of purpose TALER_SIGNATURE_PURSE_MERGE';
+CREATE TABLE IF NOT EXISTS wad_out_entries_default
+ PARTITION OF wad_out_entries
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_wad_out_entries_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE wad_out_entries_' || partition_suffix || ' '
+ 'ADD CONSTRAINT wad_out_entries_' || partition_suffix || '_wad_out_entry_serial_id_key '
+ 'UNIQUE (wad_out_entry_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_wad_out_entries_partition('default');
+
+
+-- ------------------------------ wads_in ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wads_in
- (wad_in_serial_id BIGSERIAL UNIQUE
+ (wad_in_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
,wad_id BYTEA PRIMARY KEY CHECK (LENGTH(wad_id)=24)
,origin_exchange_url TEXT NOT NULL
,amount_val INT8 NOT NULL
,amount_frac INT4 NOT NULL
,arrival_time INT8 NOT NULL
,UNIQUE (wad_id, origin_exchange_url)
- ); -- partition by wad_id
+ )
+ PARTITION BY HASH (wad_id);
COMMENT ON TABLE wads_in
IS 'Incoming exchange-to-exchange wad wire transfers';
COMMENT ON COLUMN wads_in.wad_id
@@ -1749,9 +1952,33 @@ COMMENT ON COLUMN wads_in.amount_val
COMMENT ON COLUMN wads_in.arrival_time
IS 'Time when the wad was received';
+CREATE TABLE IF NOT EXISTS wads_in_default
+ PARTITION OF wads_in
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_wads_in_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE wads_in_' || partition_suffix || ' '
+ 'ADD CONSTRAINT wads_in_' || partition_suffix || '_wad_in_serial_id_key '
+ 'UNIQUE (wad_in_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_wads_in_partition('default');
+
+
+-- ------------------------------ wads_in_entries ----------------------------------------
+
CREATE TABLE IF NOT EXISTS wad_in_entries
- (wad_in_entry_serial_id BIGSERIAL UNIQUE
- ,wad_in_serial_id INT8 REFERENCES wads_in (wad_in_serial_id) ON DELETE CASCADE
+ (wad_in_entry_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY --UNIQUE
+ ,wad_in_serial_id INT8 -- REFERENCES wads_in (wad_in_serial_id) ON DELETE CASCADE
,reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32)
,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32)
,h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64)
@@ -1765,7 +1992,8 @@ CREATE TABLE IF NOT EXISTS wad_in_entries
,deposit_fees_frac INT4 NOT NULL
,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)
,purse_sig BYTEA NOT NULL CHECK (LENGTH(purse_sig)=64)
- ); -- partition by purse or reserve? likely need both (so extra table?)
+ )
+ PARTITION BY HASH (purse_pub);
COMMENT ON TABLE wad_in_entries
IS 'list of purses aggregated in a wad according to the sending exchange';
COMMENT ON COLUMN wad_in_entries.wad_in_serial_id
@@ -1790,15 +2018,36 @@ COMMENT ON COLUMN wad_in_entries.reserve_sig
IS 'Signature by the receiving reserve, of purpose TALER_SIGNATURE_ACCOUNT_MERGE';
COMMENT ON COLUMN wad_in_entries.purse_sig
IS 'Signature by the purse of purpose TALER_SIGNATURE_PURSE_MERGE';
-CREATE INDEX IF NOT EXISTS wad_in_entries_wad_in_serial
- ON wad_in_entries (wad_in_serial_id);
+-- FIXME: convert to materialized index!
CREATE INDEX IF NOT EXISTS wad_in_entries_reserve_pub
ON wad_in_entries (reserve_pub);
-COMMENT ON INDEX wad_in_entries_wad_in_serial
- IS 'needed to lookup all transfers associated with a wad';
COMMENT ON INDEX wad_in_entries_reserve_pub
IS 'needed to compute reserve history';
+CREATE TABLE IF NOT EXISTS wad_in_entries_default
+ PARTITION OF wad_in_entries
+ FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+
+CREATE OR REPLACE FUNCTION add_constraints_to_wad_in_entries_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE wad_in_entries_' || partition_suffix || ' '
+ 'ADD CONSTRAINT wad_in_entries_' || partition_suffix || '_wad_in_entry_serial_id_key '
+ 'UNIQUE (wad_in_entry_serial_id) '
+ );
+END
+$$;
+
+SELECT add_constraints_to_wad_in_entries_partition('default');
+
+
+-- ------------------------------ partner_accounts ----------------------------------------
+
CREATE TABLE IF NOT EXISTS partner_accounts
(payto_uri VARCHAR PRIMARY KEY
,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE
@@ -1817,8 +2066,9 @@ COMMENT ON COLUMN partner_accounts.last_seen
IS 'Last time we saw this account as being active at the partner exchange. Used to select the most recent entry, and to detect when we should check again.';
--- Stored procedures
-
+---------------------------------------------------------------------------
+-- Stored procedures
+---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION exchange_do_withdraw(
IN cs_nonce BYTEA,
@@ -2419,9 +2669,6 @@ THEN
END IF;
END IF;
-
-
-
-- Everything fine, return success!
out_balance_ok=TRUE;
out_noreveal_index=in_noreveal_index;
@@ -2492,7 +2739,6 @@ THEN
RETURN;
END IF;
-
INSERT INTO refunds
(deposit_serial_id
,shard
@@ -2542,7 +2788,6 @@ THEN
RETURN;
END IF;
-
IF out_gone
THEN
-- money already sent to the merchant. Tough luck.
@@ -2552,8 +2797,6 @@ THEN
RETURN;
END IF;
-
-
-- Check refund balance invariant.
SELECT
SUM(amount_with_fee_val) -- overflow here is not plausible
@@ -2629,6 +2872,8 @@ END $$;
-- IS 'Executes a refund operation, checking that the corresponding deposit was sufficient to cover the refunded amount';
+
+
CREATE OR REPLACE FUNCTION exchange_do_recoup_to_reserve(
IN in_reserve_pub BYTEA,
IN in_reserve_out_serial_id INT8,
@@ -3090,6 +3335,9 @@ BEGIN
END $$;
+-------------------------------------------------------------
+-- THE END
+-------------------------------------------------------------
-- Complete transaction
COMMIT;
diff --git a/src/exchangedb/exchangedb.conf b/src/exchangedb/exchangedb.conf
index 77748bf5a..1c22301ad 100644
--- a/src/exchangedb/exchangedb.conf
+++ b/src/exchangedb/exchangedb.conf
@@ -26,3 +26,7 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
# After how long do we forget about reserves? Should be above
# the legal expiration timeframe of withdrawn coins.
LEGAL_RESERVE_EXPIRATION_TIME = 7 years
+
+# What is the desired delay between a transaction being ready and the
+# aggregator triggering on it?
+AGGREGATOR_SHIFT = 1 s
diff --git a/src/exchangedb/partition-0001.sql b/src/exchangedb/partition-0001.sql
index 49f865db1..ba3267988 100644
--- a/src/exchangedb/partition-0001.sql
+++ b/src/exchangedb/partition-0001.sql
@@ -229,6 +229,28 @@ BEGIN
);
PERFORM add_constraints_to_deposits_partition(num_partitions::varchar);
+-- TODO: dynamically (!) creating/deleting deposits partitions:
+-- create new partitions 'as needed', drop old ones once the aggregator has made
+-- them empty; as 'new' deposits will always have deadlines in the future, this
+-- would basically guarantee no conflict between aggregator and exchange service!
+-- SEE also: https://www.cybertec-postgresql.com/en/automatic-partition-creation-in-postgresql/
+-- (article is slightly wrong, as this works:)
+--CREATE TABLE tab (
+-- id bigint GENERATED ALWAYS AS IDENTITY,
+-- ts timestamp NOT NULL,
+-- data text
+-- PARTITION BY LIST ((ts::date));
+-- CREATE TABLE tab_def PARTITION OF tab DEFAULT;
+-- BEGIN
+-- CREATE TABLE tab_part2 (LIKE tab);
+-- insert into tab_part2 (id,ts, data) values (5,'2022-03-21', 'foo');
+-- alter table tab attach partition tab_part2 for values in ('2022-03-21');
+-- commit;
+-- Naturally, to ensure this is actually 100% conflict-free, we'd
+-- need to create tables at the granularity of the wire/refund deadlines;
+-- that is right now configurable via AGGREGATOR_SHIFT option.
+
+
PERFORM create_table_partition(
'refunds'
,modulus
@@ -287,4 +309,4 @@ BEGIN
END
$$;
-COMMIT; \ No newline at end of file
+COMMIT;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 3cde97732..8dc201a20 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -116,6 +116,12 @@ struct PostgresClosure
struct GNUNET_TIME_Relative legal_reserve_expiration_time;
/**
+ * What delay should we introduce before ready transactions
+ * are actually aggregated?
+ */
+ struct GNUNET_TIME_Relative aggregator_shift;
+
+ /**
* Which currency should we assume all amounts to be in?
*/
char *currency;
@@ -5993,7 +5999,8 @@ postgres_get_ready_deposit (void *cls,
};
enum GNUNET_DB_QueryStatus qs;
- now = GNUNET_TIME_absolute_get ();
+ now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
+ pg->aggregator_shift);
GNUNET_assert (start_shard_row < end_shard_row);
GNUNET_assert (end_shard_row <= INT32_MAX);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -6154,7 +6161,7 @@ postgres_iterate_matching_deposits (
uint32_t limit)
{
struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ struct GNUNET_TIME_Absolute now = {0};
uint64_t shard = compute_shard (merchant_pub);
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
@@ -6173,6 +6180,8 @@ postgres_iterate_matching_deposits (
};
enum GNUNET_DB_QueryStatus qs;
+ now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
+ pg->aggregator_shift);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"deposits_iterate_matching",
params,
@@ -13013,6 +13022,17 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
return NULL;
}
if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "exchangedb",
+ "AGGREGATOR_SHIFT",
+ &pg->aggregator_shift))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ "exchangedb",
+ "AGGREGATOR_SHIFT");
+ }
+
+ if (GNUNET_OK !=
TALER_config_get_currency (cfg,
&pg->currency))
{
diff --git a/src/exchangedb/test-exchange-db-postgres.conf b/src/exchangedb/test-exchange-db-postgres.conf
index e582025b1..ab70bcfce 100644
--- a/src/exchangedb/test-exchange-db-postgres.conf
+++ b/src/exchangedb/test-exchange-db-postgres.conf
@@ -28,3 +28,6 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
# After how long do we forget about reserves? Should be above
# the legal expiration timeframe of withdrawn coins.
LEGAL_RESERVE_EXPIRATION_TIME = 7 years
+
+# Shift to apply before aggregating.
+AGGREGATOR_SHIFT = 1s \ No newline at end of file