diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-06-21 11:47:34 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-06-21 11:47:34 +0200 |
commit | c9a928fe357a9f2c9e9b679ea18f3b394b492031 (patch) | |
tree | 60a0afb29e71f76f54b525644575dbba0d2345e6 | |
parent | de41998d55b6b419fded6ec443182d6de9bf3719 (diff) | |
download | exchange-c9a928fe357a9f2c9e9b679ea18f3b394b492031.tar.gz exchange-c9a928fe357a9f2c9e9b679ea18f3b394b492031.zip |
make insert transaction more optimistic, may reduce conflicts
-rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 128 |
1 files changed, 79 insertions, 49 deletions
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 6536bdc35..d06fc6a1e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c | |||
@@ -436,7 +436,8 @@ postgres_get_session (void *cls) | |||
436 | ",expiration_date" | 436 | ",expiration_date" |
437 | ",gc_date" | 437 | ",gc_date" |
438 | ") VALUES " | 438 | ") VALUES " |
439 | "($1, $2, $3, $4, $5, $6);", | 439 | "($1, $2, $3, $4, $5, $6)" |
440 | " ON CONFLICT DO NOTHING;", | ||
440 | 6), | 441 | 6), |
441 | /* Used in #postgres_insert_reserve_closed() */ | 442 | /* Used in #postgres_insert_reserve_closed() */ |
442 | GNUNET_PQ_make_prepare ("reserves_close_insert", | 443 | GNUNET_PQ_make_prepare ("reserves_close_insert", |
@@ -3442,36 +3443,25 @@ postgres_reserves_in_insert (void *cls, | |||
3442 | uint64_t wire_ref) | 3443 | uint64_t wire_ref) |
3443 | { | 3444 | { |
3444 | struct PostgresClosure *pg = cls; | 3445 | struct PostgresClosure *pg = cls; |
3445 | enum GNUNET_DB_QueryStatus reserve_exists; | 3446 | enum GNUNET_DB_QueryStatus qs1; |
3446 | enum GNUNET_DB_QueryStatus qs; | ||
3447 | struct TALER_EXCHANGEDB_Reserve reserve; | 3447 | struct TALER_EXCHANGEDB_Reserve reserve; |
3448 | struct GNUNET_TIME_Absolute expiry; | 3448 | struct GNUNET_TIME_Absolute expiry; |
3449 | 3449 | ||
3450 | reserve.pub = *reserve_pub; | 3450 | reserve.pub = *reserve_pub; |
3451 | reserve_exists = postgres_reserves_get (cls, | 3451 | expiry = GNUNET_TIME_absolute_add (execution_time, |
3452 | session, | 3452 | pg->idle_reserve_expiration_time); |
3453 | &reserve); | 3453 | (void) GNUNET_TIME_round_abs (&expiry); |
3454 | if (0 > reserve_exists) | ||
3455 | { | ||
3456 | GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == reserve_exists); | ||
3457 | return reserve_exists; | ||
3458 | } | ||
3459 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 3454 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
3460 | "Creating reserve %s with expiration in %s\n", | 3455 | "Creating reserve %s with expiration in %s\n", |
3461 | TALER_B2S (reserve_pub), | 3456 | TALER_B2S (reserve_pub), |
3462 | GNUNET_STRINGS_relative_time_to_string ( | 3457 | GNUNET_STRINGS_relative_time_to_string ( |
3463 | pg->idle_reserve_expiration_time, | 3458 | pg->idle_reserve_expiration_time, |
3464 | GNUNET_NO)); | 3459 | GNUNET_NO)); |
3465 | expiry = GNUNET_TIME_absolute_add (execution_time, | 3460 | /* Optimistically assume this is a new reserve, create balance for the first |
3466 | pg->idle_reserve_expiration_time); | 3461 | time; we do this before adding the actual transaction to "reserves_in", |
3467 | (void) GNUNET_TIME_round_abs (&expiry); | 3462 | as for a new reserve it can't be a duplicate 'add' operation, and as |
3468 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == reserve_exists) | 3463 | the 'add' operation may need the reserve entry as a foreign key. */ |
3469 | { | 3464 | { |
3470 | /* New reserve, create balance for the first time; we do this | ||
3471 | before adding the actual transaction to "reserves_in", as | ||
3472 | for a new reserve it can't be a duplicate 'add' operation, | ||
3473 | and as the 'add' operation may need the reserve entry | ||
3474 | as a foreign key. */ | ||
3475 | struct GNUNET_PQ_QueryParam params[] = { | 3465 | struct GNUNET_PQ_QueryParam params[] = { |
3476 | GNUNET_PQ_query_param_auto_from_type (reserve_pub), | 3466 | GNUNET_PQ_query_param_auto_from_type (reserve_pub), |
3477 | GNUNET_PQ_query_param_string (sender_account_details), | 3467 | GNUNET_PQ_query_param_string (sender_account_details), |
@@ -3483,24 +3473,16 @@ postgres_reserves_in_insert (void *cls, | |||
3483 | 3473 | ||
3484 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 3474 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
3485 | "Reserve does not exist; creating a new one\n"); | 3475 | "Reserve does not exist; creating a new one\n"); |
3486 | qs = GNUNET_PQ_eval_prepared_non_select (session->conn, | 3476 | /* Note: query uses 'on conflict do nothing' */ |
3487 | "reserve_create", | 3477 | qs1 = GNUNET_PQ_eval_prepared_non_select (session->conn, |
3488 | params); | 3478 | "reserve_create", |
3489 | if (0 > qs) | 3479 | params); |
3490 | { | 3480 | if (qs1 < 0) |
3491 | GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); | 3481 | return qs1; |
3492 | return qs; | ||
3493 | } | ||
3494 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
3495 | { | ||
3496 | /* Maybe DB did not detect serializiability error already, | ||
3497 | but clearly there must be one. Still odd. */ | ||
3498 | GNUNET_break (0); | ||
3499 | return GNUNET_DB_STATUS_SOFT_ERROR; | ||
3500 | } | ||
3501 | } | 3482 | } |
3483 | |||
3502 | /* Create new incoming transaction, "ON CONFLICT DO NOTHING" | 3484 | /* Create new incoming transaction, "ON CONFLICT DO NOTHING" |
3503 | is used to guard against duplicates. */ | 3485 | is again used to guard against duplicates. */ |
3504 | { | 3486 | { |
3505 | struct GNUNET_PQ_QueryParam params[] = { | 3487 | struct GNUNET_PQ_QueryParam params[] = { |
3506 | GNUNET_PQ_query_param_auto_from_type (&reserve.pub), | 3488 | GNUNET_PQ_query_param_auto_from_type (&reserve.pub), |
@@ -3511,26 +3493,59 @@ postgres_reserves_in_insert (void *cls, | |||
3511 | TALER_PQ_query_param_absolute_time (&execution_time), | 3493 | TALER_PQ_query_param_absolute_time (&execution_time), |
3512 | GNUNET_PQ_query_param_end | 3494 | GNUNET_PQ_query_param_end |
3513 | }; | 3495 | }; |
3496 | enum GNUNET_DB_QueryStatus qs2; | ||
3514 | 3497 | ||
3515 | qs = GNUNET_PQ_eval_prepared_non_select (session->conn, | 3498 | qs2 = GNUNET_PQ_eval_prepared_non_select (session->conn, |
3516 | "reserves_in_add_transaction", | 3499 | "reserves_in_add_transaction", |
3517 | params); | 3500 | params); |
3518 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) | 3501 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs2) |
3519 | { | 3502 | { |
3520 | GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); | 3503 | GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs2); |
3521 | return qs; | 3504 | return qs2; |
3505 | } | ||
3506 | if (0 >= qs2) | ||
3507 | { | ||
3508 | /* Transaction was already known or error. We are finished. */ | ||
3509 | return qs2; | ||
3510 | } | ||
3511 | } | ||
3512 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs1) | ||
3513 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* new reserve, we are finished */ | ||
3514 | |||
3515 | /* we were wrong with our optimistic assumption: | ||
3516 | reserve does exist, need to do an update instead */ | ||
3517 | { | ||
3518 | enum GNUNET_DB_QueryStatus reserve_exists; | ||
3519 | |||
3520 | reserve_exists = postgres_reserves_get (cls, | ||
3521 | session, | ||
3522 | &reserve); | ||
3523 | switch (reserve_exists) | ||
3524 | { | ||
3525 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
3526 | GNUNET_break (0); | ||
3527 | return reserve_exists; | ||
3528 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
3529 | return reserve_exists; | ||
3530 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
3531 | /* First we got a conflict, but then we cannot select? Very strange. */ | ||
3532 | GNUNET_break (0); | ||
3533 | return GNUNET_DB_STATUS_SOFT_ERROR; | ||
3534 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
3535 | /* continued below */ | ||
3536 | break; | ||
3522 | } | 3537 | } |
3523 | } | 3538 | } |
3524 | 3539 | ||
3525 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == reserve_exists) | ||
3526 | { | 3540 | { |
3541 | struct TALER_EXCHANGEDB_Reserve updated_reserve; | ||
3542 | enum GNUNET_DB_QueryStatus qs3; | ||
3543 | |||
3527 | /* If the reserve already existed, we need to still update the | 3544 | /* If the reserve already existed, we need to still update the |
3528 | balance; we do this after checking for duplication, as | 3545 | balance; we do this after checking for duplication, as |
3529 | otherwise we might have to actually pay the cost to roll this | 3546 | otherwise we might have to actually pay the cost to roll this |
3530 | back for duplicate transactions; like this, we should virtually | 3547 | back for duplicate transactions; like this, we should virtually |
3531 | never actually have to rollback anything. */ | 3548 | never actually have to rollback anything. */ |
3532 | struct TALER_EXCHANGEDB_Reserve updated_reserve; | ||
3533 | |||
3534 | updated_reserve.pub = reserve.pub; | 3549 | updated_reserve.pub = reserve.pub; |
3535 | if (0 > | 3550 | if (0 > |
3536 | TALER_amount_add (&updated_reserve.balance, | 3551 | TALER_amount_add (&updated_reserve.balance, |
@@ -3548,11 +3563,26 @@ postgres_reserves_in_insert (void *cls, | |||
3548 | updated_reserve.gc = GNUNET_TIME_absolute_max (updated_reserve.expiry, | 3563 | updated_reserve.gc = GNUNET_TIME_absolute_max (updated_reserve.expiry, |
3549 | reserve.gc); | 3564 | reserve.gc); |
3550 | (void) GNUNET_TIME_round_abs (&updated_reserve.gc); | 3565 | (void) GNUNET_TIME_round_abs (&updated_reserve.gc); |
3551 | return reserves_update (cls, | 3566 | qs3 = reserves_update (cls, |
3552 | session, | 3567 | session, |
3553 | &updated_reserve); | 3568 | &updated_reserve); |
3569 | switch (qs3) | ||
3570 | { | ||
3571 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
3572 | GNUNET_break (0); | ||
3573 | return qs3; | ||
3574 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
3575 | return qs3; | ||
3576 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
3577 | /* How can the UPDATE not work here? Very strange. */ | ||
3578 | GNUNET_break (0); | ||
3579 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
3580 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
3581 | /* continued below */ | ||
3582 | break; | ||
3583 | } | ||
3584 | return qs3; | ||
3554 | } | 3585 | } |
3555 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; | ||
3556 | } | 3586 | } |
3557 | 3587 | ||
3558 | 3588 | ||