diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 30af20d0..216916a2 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -17,6 +17,7 @@ #include #include "Base.h" #include "util.h" +#include "Timestamp.h" #include "Identity.h" #include "FS.h" #include "Log.h" @@ -52,6 +53,7 @@ namespace client std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) override; void AddAddress (std::shared_ptr address) override; void RemoveAddress (const i2p::data::IdentHash& ident) override; + void CleanUpCache () override; bool Init () override; int Load (Addresses& addresses) override; @@ -61,7 +63,7 @@ namespace client void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified) override; bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) override; void ResetEtags () override; - + private: int LoadFromFile (const std::string& filename, Addresses& addresses); // returns -1 if can't open file, otherwise number of records @@ -72,7 +74,8 @@ namespace client std::string etagsPath, indexPath, localPath; bool m_IsPersist; std::string m_HostsFile; // file to dump hosts.txt, empty if not used - std::unordered_map > m_FullAddressesCache; // ident hash -> full ident buffer + std::unordered_map, uint64_t> > m_FullAddressCache; // ident hash -> (full ident buffer, last access timestamp) + std::mutex m_FullAddressCacheMutex; }; bool AddressBookFilesystemStorage::Init() @@ -95,9 +98,16 @@ namespace client std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) { - auto it = m_FullAddressesCache.find (ident); - if (it != m_FullAddressesCache.end ()) - return std::make_shared(it->second.data (), it->second.size ()); + auto ts = i2p::util::GetMonotonicSeconds (); + { + std::lock_guard l(m_FullAddressCacheMutex); + auto it = m_FullAddressCache.find (ident); + if (it != m_FullAddressCache.end ()) + { + it->second.second = ts; + return std::make_shared(it->second.first.data (), it->second.first.size ()); + } + } if (!m_IsPersist) { @@ -127,7 +137,10 @@ namespace client LogPrint (eLogError, "Addressbook: Couldn't read ", filename); return nullptr; } - m_FullAddressesCache.try_emplace (ident, buf); + { + std::lock_guard l(m_FullAddressCacheMutex); + m_FullAddressCache.try_emplace (ident, buf, ts); + } return std::make_shared(buf.data (), len); } @@ -135,26 +148,35 @@ namespace client { if (!address) return; size_t len = address->GetFullLen (); + std::vector buf; if (!len) return; // invalid address - auto [it, inserted] = m_FullAddressesCache.try_emplace (address->GetIdentHash(), len); - if (inserted) - { - address->ToBuffer (it->second.data (), len); - if (!m_IsPersist) return; - std::string path = storage.Path( address->GetIdentHash().ToBase32() ); + { + std::lock_guard l(m_FullAddressCacheMutex); + auto [it, inserted] = m_FullAddressCache.try_emplace (address->GetIdentHash(), len, i2p::util::GetMonotonicSeconds ()); + if (inserted) + address->ToBuffer (it->second.first.data (), len); + if (m_IsPersist) + buf = it->second.first; + } + if (m_IsPersist && !buf.empty ()) + { + std::string path = storage.Path(address->GetIdentHash().ToBase32()); std::ofstream f (path, std::ofstream::binary | std::ofstream::out); if (!f.is_open ()) { LogPrint (eLogError, "Addressbook: Can't open file ", path); return; } - f.write ((const char *)it->second.data (), len); + f.write ((const char *)buf.data (), len); } } void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) { - m_FullAddressesCache.erase (ident); + { + std::lock_guard l(m_FullAddressCacheMutex); + m_FullAddressCache.erase (ident); + } if (!m_IsPersist) return; storage.Remove( ident.ToBase32() ); } @@ -298,6 +320,19 @@ namespace client } } + void AddressBookFilesystemStorage::CleanUpCache () + { + auto ts = i2p::util::GetMonotonicSeconds (); + std::lock_guard l(m_FullAddressCacheMutex); + for (auto it = m_FullAddressCache.begin (); it != m_FullAddressCache.end ();) + { + if (ts > it->second.second + ADDRESS_CACHE_EXPIRATION_TIMEOUT) + it = m_FullAddressCache.erase (it); + else + it++; + } + } + //--------------------------------------------------------------------- Address::Address (std::string_view b32): @@ -344,6 +379,7 @@ namespace client LoadHosts (); /* try storage, then hosts.txt, then download */ StartSubscriptions (); StartLookups (); + ScheduleCacheUpdate (); } } @@ -361,6 +397,11 @@ namespace client m_SubscriptionsUpdateTimer->cancel (); m_SubscriptionsUpdateTimer = nullptr; } + if (m_AddressCacheUpdateTimer) + { + m_AddressCacheUpdateTimer->cancel (); + m_AddressCacheUpdateTimer = nullptr; + } bool isDownloading = m_Downloading.valid (); if (isDownloading) { @@ -850,6 +891,29 @@ namespace client } } + void AddressBook::ScheduleCacheUpdate () + { + if (!m_AddressCacheUpdateTimer) + { + auto dest = i2p::client::context.GetSharedLocalDestination (); + if(dest) + m_AddressCacheUpdateTimer = std::make_unique(dest->GetService ()); + } + if (m_AddressCacheUpdateTimer) + { + m_AddressCacheUpdateTimer->expires_from_now (boost::posix_time::seconds(ADDRESS_CACHE_UPDATE_INTERVAL )); + m_SubscriptionsUpdateTimer->async_wait ( + [this](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_Storage) m_Storage->CleanUpCache (); + ScheduleCacheUpdate (); + } + }); + } + } + AddressBookSubscription::AddressBookSubscription (AddressBook& book, std::string_view link): m_Book (book), m_Link (link) { diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index 70c567d7..8b32aa93 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -34,7 +34,9 @@ namespace client const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes const int CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES = 10; // then update timeout - const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in second + const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in seconds + const int ADDRESS_CACHE_EXPIRATION_TIMEOUT = 710; // in seconds + const int ADDRESS_CACHE_UPDATE_INTERVAL = 76; // in seconds const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; @@ -65,6 +67,7 @@ namespace client virtual std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) = 0; virtual void AddAddress (std::shared_ptr address) = 0; virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; + virtual void CleanUpCache () = 0; virtual bool Init () = 0; virtual int Load (Addresses& addresses) = 0; @@ -120,6 +123,8 @@ namespace client void StopLookups (); void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void ScheduleCacheUpdate (); + private: std::mutex m_AddressBookMutex; @@ -133,7 +138,7 @@ namespace client int m_NumRetries; std::vector > m_Subscriptions; std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet - std::unique_ptr m_SubscriptionsUpdateTimer; + std::unique_ptr m_SubscriptionsUpdateTimer, m_AddressCacheUpdateTimer; bool m_IsEnabled; };