Optimisation: Store transaction list order in memory rather than compute it every need
Huge performance improvement (450%) for zapwallettxes
This commit is contained in:
parent
eac53ec992
commit
3e7c89196c
6 changed files with 44 additions and 52 deletions
|
@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
ae.nTime = 1333333333;
|
ae.nTime = 1333333333;
|
||||||
ae.strOtherAccount = "b";
|
ae.strOtherAccount = "b";
|
||||||
ae.strComment = "";
|
ae.strComment = "";
|
||||||
walletdb.WriteAccountingEntry(ae);
|
pwalletMain->AddAccountingEntry(ae, walletdb);
|
||||||
|
|
||||||
wtx.mapValue["comment"] = "z";
|
wtx.mapValue["comment"] = "z";
|
||||||
pwalletMain->AddToWallet(wtx, false, &walletdb);
|
pwalletMain->AddToWallet(wtx, false, &walletdb);
|
||||||
|
@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
|
|
||||||
ae.nTime = 1333333336;
|
ae.nTime = 1333333336;
|
||||||
ae.strOtherAccount = "c";
|
ae.strOtherAccount = "c";
|
||||||
walletdb.WriteAccountingEntry(ae);
|
pwalletMain->AddAccountingEntry(ae, walletdb);
|
||||||
|
|
||||||
GetResults(walletdb, results);
|
GetResults(walletdb, results);
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
ae.nTime = 1333333330;
|
ae.nTime = 1333333330;
|
||||||
ae.strOtherAccount = "d";
|
ae.strOtherAccount = "d";
|
||||||
ae.nOrderPos = pwalletMain->IncOrderPosNext();
|
ae.nOrderPos = pwalletMain->IncOrderPosNext();
|
||||||
walletdb.WriteAccountingEntry(ae);
|
pwalletMain->AddAccountingEntry(ae, walletdb);
|
||||||
|
|
||||||
GetResults(walletdb, results);
|
GetResults(walletdb, results);
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
ae.nTime = 1333333334;
|
ae.nTime = 1333333334;
|
||||||
ae.strOtherAccount = "e";
|
ae.strOtherAccount = "e";
|
||||||
ae.nOrderPos = -1;
|
ae.nOrderPos = -1;
|
||||||
walletdb.WriteAccountingEntry(ae);
|
pwalletMain->AddAccountingEntry(ae, walletdb);
|
||||||
|
|
||||||
GetResults(walletdb, results);
|
GetResults(walletdb, results);
|
||||||
|
|
||||||
|
|
|
@ -835,7 +835,7 @@ UniValue movecmd(const UniValue& params, bool fHelp)
|
||||||
debit.nTime = nNow;
|
debit.nTime = nNow;
|
||||||
debit.strOtherAccount = strTo;
|
debit.strOtherAccount = strTo;
|
||||||
debit.strComment = strComment;
|
debit.strComment = strComment;
|
||||||
walletdb.WriteAccountingEntry(debit);
|
pwalletMain->AddAccountingEntry(debit, walletdb);
|
||||||
|
|
||||||
// Credit
|
// Credit
|
||||||
CAccountingEntry credit;
|
CAccountingEntry credit;
|
||||||
|
@ -845,7 +845,7 @@ UniValue movecmd(const UniValue& params, bool fHelp)
|
||||||
credit.nTime = nNow;
|
credit.nTime = nNow;
|
||||||
credit.strOtherAccount = strFrom;
|
credit.strOtherAccount = strFrom;
|
||||||
credit.strComment = strComment;
|
credit.strComment = strComment;
|
||||||
walletdb.WriteAccountingEntry(credit);
|
pwalletMain->AddAccountingEntry(credit, walletdb);
|
||||||
|
|
||||||
if (!walletdb.TxnCommit())
|
if (!walletdb.TxnCommit())
|
||||||
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
|
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
|
||||||
|
@ -1470,11 +1470,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
|
||||||
|
|
||||||
UniValue ret(UniValue::VARR);
|
UniValue ret(UniValue::VARR);
|
||||||
|
|
||||||
std::list<CAccountingEntry> acentries;
|
const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered;
|
||||||
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
|
|
||||||
|
|
||||||
// iterate backwards until we have nCount items to return:
|
// iterate backwards until we have nCount items to return:
|
||||||
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||||
{
|
{
|
||||||
CWalletTx *const pwtx = (*it).second.first;
|
CWalletTx *const pwtx = (*it).second.first;
|
||||||
if (pwtx != 0)
|
if (pwtx != 0)
|
||||||
|
@ -1579,8 +1578,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list<CAccountingEntry> acentries;
|
const list<CAccountingEntry> & acentries = pwalletMain->laccentries;
|
||||||
CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
|
|
||||||
BOOST_FOREACH(const CAccountingEntry& entry, acentries)
|
BOOST_FOREACH(const CAccountingEntry& entry, acentries)
|
||||||
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
|
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
|
||||||
|
|
||||||
|
|
|
@ -588,31 +588,6 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
|
||||||
return nRet;
|
return nRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
|
|
||||||
{
|
|
||||||
AssertLockHeld(cs_wallet); // mapWallet
|
|
||||||
CWalletDB walletdb(strWalletFile);
|
|
||||||
|
|
||||||
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
|
|
||||||
TxItems txOrdered;
|
|
||||||
|
|
||||||
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
|
|
||||||
// would make this much faster for applications that do this a lot.
|
|
||||||
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
|
||||||
{
|
|
||||||
CWalletTx* wtx = &((*it).second);
|
|
||||||
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
|
|
||||||
}
|
|
||||||
acentries.clear();
|
|
||||||
walletdb.ListAccountCreditDebit(strAccount, acentries);
|
|
||||||
BOOST_FOREACH(CAccountingEntry& entry, acentries)
|
|
||||||
{
|
|
||||||
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return txOrdered;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWallet::MarkDirty()
|
void CWallet::MarkDirty()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -629,7 +604,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
|
||||||
if (fFromLoadWallet)
|
if (fFromLoadWallet)
|
||||||
{
|
{
|
||||||
mapWallet[hash] = wtxIn;
|
mapWallet[hash] = wtxIn;
|
||||||
mapWallet[hash].BindWallet(this);
|
CWalletTx& wtx = mapWallet[hash];
|
||||||
|
wtx.BindWallet(this);
|
||||||
|
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
|
||||||
AddToSpends(hash);
|
AddToSpends(hash);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -644,6 +621,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
|
||||||
{
|
{
|
||||||
wtx.nTimeReceived = GetAdjustedTime();
|
wtx.nTimeReceived = GetAdjustedTime();
|
||||||
wtx.nOrderPos = IncOrderPosNext(pwalletdb);
|
wtx.nOrderPos = IncOrderPosNext(pwalletdb);
|
||||||
|
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
|
||||||
|
|
||||||
wtx.nTimeSmart = wtx.nTimeReceived;
|
wtx.nTimeSmart = wtx.nTimeReceived;
|
||||||
if (!wtxIn.hashBlock.IsNull())
|
if (!wtxIn.hashBlock.IsNull())
|
||||||
|
@ -655,9 +633,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
|
||||||
{
|
{
|
||||||
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
|
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
|
||||||
int64_t latestTolerated = latestNow + 300;
|
int64_t latestTolerated = latestNow + 300;
|
||||||
std::list<CAccountingEntry> acentries;
|
const TxItems & txOrdered = wtxOrdered;
|
||||||
TxItems txOrdered = OrderedTxItems(acentries);
|
for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||||
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
|
||||||
{
|
{
|
||||||
CWalletTx *const pwtx = (*it).second.first;
|
CWalletTx *const pwtx = (*it).second.first;
|
||||||
if (pwtx == &wtx)
|
if (pwtx == &wtx)
|
||||||
|
@ -2118,6 +2095,18 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB & pwalletdb)
|
||||||
|
{
|
||||||
|
if (!pwalletdb.WriteAccountingEntry_Backend(acentry))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
laccentries.push_back(acentry);
|
||||||
|
CAccountingEntry & entry = laccentries.back();
|
||||||
|
wtxOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
|
CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
|
||||||
{
|
{
|
||||||
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
|
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
|
||||||
|
|
|
@ -531,6 +531,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<uint256, CWalletTx> mapWallet;
|
std::map<uint256, CWalletTx> mapWallet;
|
||||||
|
std::list<CAccountingEntry> laccentries;
|
||||||
|
|
||||||
|
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
|
||||||
|
typedef std::multimap<int64_t, TxPair > TxItems;
|
||||||
|
TxItems wtxOrdered;
|
||||||
|
|
||||||
int64_t nOrderPosNext;
|
int64_t nOrderPosNext;
|
||||||
std::map<uint256, int> mapRequestCount;
|
std::map<uint256, int> mapRequestCount;
|
||||||
|
@ -617,16 +622,6 @@ public:
|
||||||
*/
|
*/
|
||||||
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);
|
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);
|
||||||
|
|
||||||
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
|
|
||||||
typedef std::multimap<int64_t, TxPair > TxItems;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the wallet's activity log
|
|
||||||
* @return multimap of ordered transactions and accounting entries
|
|
||||||
* @warning Returned pointers are *only* valid within the scope of passed acentries
|
|
||||||
*/
|
|
||||||
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
|
|
||||||
|
|
||||||
void MarkDirty();
|
void MarkDirty();
|
||||||
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
|
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
|
||||||
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
||||||
|
@ -656,6 +651,8 @@ public:
|
||||||
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||||
|
|
||||||
|
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);
|
||||||
|
|
||||||
static CFeeRate minTxFee;
|
static CFeeRate minTxFee;
|
||||||
/**
|
/**
|
||||||
* Estimate the minimum fee considering user set parameters
|
* Estimate the minimum fee considering user set parameters
|
||||||
|
|
|
@ -191,7 +191,7 @@ bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccount
|
||||||
return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
|
return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
|
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
|
||||||
{
|
{
|
||||||
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
|
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
|
||||||
}
|
}
|
||||||
|
@ -709,6 +709,12 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
||||||
if (wss.fAnyUnordered)
|
if (wss.fAnyUnordered)
|
||||||
result = ReorderTransactions(pwallet);
|
result = ReorderTransactions(pwallet);
|
||||||
|
|
||||||
|
pwallet->laccentries.clear();
|
||||||
|
ListAccountCreditDebit("*", pwallet->laccentries);
|
||||||
|
BOOST_FOREACH(CAccountingEntry& entry, pwallet->laccentries) {
|
||||||
|
pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry)));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,9 @@ public:
|
||||||
|
|
||||||
bool WriteMinVersion(int nVersion);
|
bool WriteMinVersion(int nVersion);
|
||||||
|
|
||||||
|
/// This writes directly to the database, and will not update the CWallet's cached accounting entries!
|
||||||
|
/// Use wallet.AddAccountingEntry instead, to write *and* update its caches.
|
||||||
|
bool WriteAccountingEntry_Backend(const CAccountingEntry& acentry);
|
||||||
bool ReadAccount(const std::string& strAccount, CAccount& account);
|
bool ReadAccount(const std::string& strAccount, CAccount& account);
|
||||||
bool WriteAccount(const std::string& strAccount, const CAccount& account);
|
bool WriteAccount(const std::string& strAccount, const CAccount& account);
|
||||||
|
|
||||||
|
@ -118,7 +121,6 @@ public:
|
||||||
/// Erase destination data tuple from wallet database
|
/// Erase destination data tuple from wallet database
|
||||||
bool EraseDestData(const std::string &address, const std::string &key);
|
bool EraseDestData(const std::string &address, const std::string &key);
|
||||||
|
|
||||||
bool WriteAccountingEntry(const CAccountingEntry& acentry);
|
|
||||||
CAmount GetAccountCreditDebit(const std::string& strAccount);
|
CAmount GetAccountCreditDebit(const std::string& strAccount);
|
||||||
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
|
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue