diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index 6f6d101..d655ad5 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -352,11 +352,13 @@ struct PendingPayout { | |||
| 352 | // lets an entry stay on disk until every address in `amounts` is paid, | 352 | // lets an entry stay on disk until every address in `amounts` is paid, |
| 353 | // instead of being erased the moment the batch is handed off. | 353 | // instead of being erased the moment the batch is handed off. |
| 354 | std::map<std::string, std::string> paidTxids; // address -> txid | 354 | std::map<std::string, std::string> paidTxids; // address -> txid |
| 355 | int retries = 0; // exec-thread only: SendMoney attempts so far | 355 | // Retry and backoff state does NOT live here. It used to be declared here |
| 356 | int deferrals = 0; // exec-thread only: waits for spendable coin | 356 | // as well as on PayingRecord, and the copy on this side was never written: |
| 357 | // Upstream's `nextAttempt` is this field under another name; ours is kept | 357 | // `retryAfter` was declared, serialised, loaded and tested at the flush |
| 358 | // so the retry/deferral policy above stays coherent with it. | 358 | // gate, and the only assignment to it anywhere was `= 0`, so the gate could |
| 359 | int64 retryAfter = 0; // exec-thread only: unix time, 0 = ready now | 359 | // not fire. `retries` and `deferrals` were the same story. The real state |
| 360 | // is per (round, recipient) -- which is the granularity a payment can fail | ||
| 361 | // at -- and it lives on PayingRecord, which is now what persists it. | ||
| 360 | }; | 362 | }; |
| 361 | static std::vector<PendingPayout> gPendingPayouts; | 363 | static std::vector<PendingPayout> gPendingPayouts; |
| 362 | static std::mutex gPayoutLedgerMutex; | 364 | static std::mutex gPayoutLedgerMutex; |
| @@ -597,7 +599,11 @@ static VersionedBlob SerializePendingPayoutsLocked() | |||
| 597 | obj["blockHeight"] = pp.blockHeight; | 599 | obj["blockHeight"] = pp.blockHeight; |
| 598 | obj["blockHash"] = pp.blockHash.ToString(); | 600 | obj["blockHash"] = pp.blockHash.ToString(); |
| 599 | obj["matureAtHeight"] = pp.matureAtHeight; | 601 | obj["matureAtHeight"] = pp.matureAtHeight; |
| 600 | obj["retryAfter"] = pp.retryAfter; | 602 | // No retryAfter: it was never written with anything but zero, and the |
| 603 | // retry clock that is actually used is on PayingRecord. Files that | ||
| 604 | // still carry the key load fine (it is ignored), and a build that | ||
| 605 | // expects it defaults an absent one to 0, which means "ready now" -- | ||
| 606 | // the same answer it always got. | ||
| 601 | nlohmann::json amounts = nlohmann::json::object(); | 607 | nlohmann::json amounts = nlohmann::json::object(); |
| 602 | for (const auto& kv : pp.amounts) | 608 | for (const auto& kv : pp.amounts) |
| 603 | amounts[kv.first] = kv.second; | 609 | amounts[kv.first] = kv.second; |
| @@ -655,10 +661,9 @@ static void LoadPendingPayouts() | |||
| 655 | // still COINBASE_MATURITY. | 661 | // still COINBASE_MATURITY. |
| 656 | if (pp.blockHeight == 0 && pp.matureAtHeight > COINBASE_MATURITY) | 662 | if (pp.blockHeight == 0 && pp.matureAtHeight > COINBASE_MATURITY) |
| 657 | pp.blockHeight = pp.matureAtHeight - COINBASE_MATURITY; | 663 | pp.blockHeight = pp.matureAtHeight - COINBASE_MATURITY; |
| 658 | // Absent in every file written before this merge; default 0 means | 664 | // "retryAfter" is deliberately not read. Files written by the |
| 659 | // "ready now", which is the right answer for a restored entry. | 665 | // merged build carry it, always as 0, and there is nowhere left to |
| 660 | if (obj.contains("retryAfter") && obj["retryAfter"].is_number()) | 666 | // put it -- the retry clock is PayingRecord's. |
| 661 | pp.retryAfter = obj["retryAfter"].get<int64>(); | ||
| 662 | for (auto it = obj["amounts"].begin(); it != obj["amounts"].end(); ++it) | 667 | for (auto it = obj["amounts"].begin(); it != obj["amounts"].end(); ++it) |
| 663 | pp.amounts[it.key()] = it.value().get<int64>(); | 668 | pp.amounts[it.key()] = it.value().get<int64>(); |
| 664 | if (obj.contains("paidTxids") && obj["paidTxids"].is_object()) | 669 | if (obj.contains("paidTxids") && obj["paidTxids"].is_object()) |
| @@ -777,6 +782,11 @@ static VersionedBlob SerializePayingLocked() | |||
| 777 | obj["amount"] = r.amount; | 782 | obj["amount"] = r.amount; |
| 778 | obj["retries"] = r.retries; | 783 | obj["retries"] = r.retries; |
| 779 | obj["deferrals"] = r.deferrals; | 784 | obj["deferrals"] = r.deferrals; |
| 785 | // The live retry clock, which used not to be written at all: every | ||
| 786 | // restart handed every backing-off payment a retryAfter of 0 and put | ||
| 787 | // the whole set into the very next 500 ms batch, so a node restarted | ||
| 788 | // in a loop retried continuously instead of once a minute. | ||
| 789 | obj["retryAfter"] = r.retryAfter; | ||
| 780 | arr.push_back(obj); | 790 | arr.push_back(obj); |
| 781 | } | 791 | } |
| 782 | VersionedBlob v; | 792 | VersionedBlob v; |
| @@ -818,6 +828,10 @@ static void LoadPaying() | |||
| 818 | r.retries = obj["retries"].get<int>(); | 828 | r.retries = obj["retries"].get<int>(); |
| 819 | if (obj.contains("deferrals") && obj["deferrals"].is_number()) | 829 | if (obj.contains("deferrals") && obj["deferrals"].is_number()) |
| 820 | r.deferrals = obj["deferrals"].get<int>(); | 830 | r.deferrals = obj["deferrals"].get<int>(); |
| 831 | // Absent in files written before this change; 0 means ready now, | ||
| 832 | // which is exactly the behaviour those files used to get. | ||
| 833 | if (obj.contains("retryAfter") && obj["retryAfter"].is_number()) | ||
| 834 | r.retryAfter = obj["retryAfter"].get<int64>(); | ||
| 821 | // Collapse duplicates as they are read. A build without the | 835 | // Collapse duplicates as they are read. A build without the |
| 822 | // ReconcilePayouts seeding below wrote a second (and after another | 836 | // ReconcilePayouts seeding below wrote a second (and after another |
| 823 | // restart, a third) byte-identical record for every unpaid | 837 | // restart, a third) byte-identical record for every unpaid |
| @@ -834,8 +848,9 @@ static void LoadPaying() | |||
| 834 | break; | 848 | break; |
| 835 | } | 849 | } |
| 836 | if (dup) { | 850 | if (dup) { |
| 837 | dup->retries = std::max(dup->retries, r.retries); | 851 | dup->retries = std::max(dup->retries, r.retries); |
| 838 | dup->deferrals = std::max(dup->deferrals, r.deferrals); | 852 | dup->deferrals = std::max(dup->deferrals, r.deferrals); |
| 853 | dup->retryAfter = std::max(dup->retryAfter, r.retryAfter); | ||
| 839 | LogPrint("payout", "[payout] collapsing duplicate in-flight" | 854 | LogPrint("payout", "[payout] collapsing duplicate in-flight" |
| 840 | " record for %s to %s -- one payment, one record\n", | 855 | " record for %s to %s -- one payment, one record\n", |
| 841 | FormatMoney(r.amount).c_str(), r.address.c_str()); | 856 | FormatMoney(r.amount).c_str(), r.address.c_str()); |
| @@ -1505,12 +1520,9 @@ static void FlushMaturePayouts() | |||
| 1505 | } | 1520 | } |
| 1506 | if (voided) ledgerSnap = SerializePendingPayoutsLocked(); | 1521 | if (voided) ledgerSnap = SerializePendingPayoutsLocked(); |
| 1507 | 1522 | ||
| 1508 | int64 now = GetTime(); | ||
| 1509 | for (const PendingPayout& pp : gPendingPayouts) { | 1523 | for (const PendingPayout& pp : gPendingPayouts) { |
| 1510 | if (nBestHeight < pp.matureAtHeight) | 1524 | if (nBestHeight < pp.matureAtHeight) |
| 1511 | continue; | 1525 | continue; |
| 1512 | if (pp.retryAfter > now) | ||
| 1513 | continue; | ||
| 1514 | if (PayoutFullyPaid(pp)) | 1526 | if (PayoutFullyPaid(pp)) |
| 1515 | continue; | 1527 | continue; |
| 1516 | // An entry with neither a hash nor a height cannot be told apart | 1528 | // An entry with neither a hash nor a height cannot be told apart |
| @@ -1658,7 +1670,6 @@ static void RecordPayoutTxid(const PayingRecord& r, const std::string& txid) | |||
| 1658 | r.blockHeight, r.blockHash)) | 1670 | r.blockHeight, r.blockHash)) |
| 1659 | continue; | 1671 | continue; |
| 1660 | it->paidTxids[r.address] = txid; | 1672 | it->paidTxids[r.address] = txid; |
| 1661 | it->retryAfter = 0; | ||
| 1662 | if (PayoutFullyPaid(*it)) { | 1673 | if (PayoutFullyPaid(*it)) { |
| 1663 | LogPrint("payout", "[payout] payout for block %d complete --" | 1674 | LogPrint("payout", "[payout] payout for block %d complete --" |
| 1664 | " removing from ledger\n", it->blockHeight); | 1675 | " removing from ledger\n", it->blockHeight); |