Skip to content

Commit de2ae67

Browse files
authored
Merge pull request #262 from jolavillette/ImproveUploadStatistics
implement time-based upload stats (V2) with configurable retention and auto-cleanup
2 parents 49a0301 + 253be0c commit de2ae67

8 files changed

Lines changed: 208 additions & 13 deletions

File tree

src/file_sharing/p3filelists.cc

Lines changed: 125 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ p3FileDatabase::p3FileDatabase(p3ServiceControl *mpeers)
8080
mLastDataRecvTS = 0 ;
8181
mTrustFriendNodesForBannedFiles = TRUST_FRIEND_NODES_FOR_BANNED_FILES_DEFAULT;
8282
mLastPrimaryBanListChangeTimeStamp = 0;
83+
mUploadStatsRetentionDays = 0;
84+
mCumulativeUploadedAll = 0;
8385

8486
// This is for the transmission of data
8587

@@ -197,6 +199,7 @@ int p3FileDatabase::tick()
197199
if(mLastCleanupTime + 5 < now)
198200
{
199201
cleanup();
202+
cleanupUploadStats(mUploadStatsRetentionDays);
200203
mLastCleanupTime = now ;
201204
}
202205

@@ -379,12 +382,12 @@ cleanup = true;
379382

380383
{
381384
RS_STACK_MUTEX(mFLSMtx) ;
382-
RsFileListsUploadStatsItem *item = nullptr;
385+
RsFileListsUploadStatsItemV2 *item = nullptr;
383386

384387
for(auto it(mCumulativeUploaded.begin());it!=mCumulativeUploaded.end();++it)
385388
{
386389
if(item == nullptr)
387-
item = new RsFileListsUploadStatsItem ;
390+
item = new RsFileListsUploadStatsItemV2 ;
388391

389392
item->hash_stats.insert(*it);
390393

@@ -497,6 +500,15 @@ cleanup = true;
497500

498501
kv.key = IGNORE_LIST_FLAGS_SS; kv.value = s; rskv->tlvkvs.pairs.push_back(kv);
499502
}
503+
{
504+
std::string s;
505+
rs_sprintf(s, "%d", mUploadStatsRetentionDays);
506+
507+
RsTlvKeyValue kv;
508+
kv.key = UPLOAD_STATS_RETENTION_DAYS_SS;
509+
kv.value = s;
510+
rskv->tlvkvs.pairs.push_back(kv);
511+
}
500512

501513
/* Add KeyValue to saveList */
502514
sList.push_back(rskv);
@@ -529,7 +541,8 @@ bool p3FileDatabase::loadList(std::list<RsItem *>& load)
529541
ignored_suffixes.push_back( ".part" );
530542
#endif
531543
mPrimaryBanList.clear();
532-
mCumulativeUploaded.clear();
544+
mCumulativeUploaded.clear();
545+
mCumulativeUploadedAll = 0;
533546

534547
for(std::list<RsItem *>::iterator it = load.begin(); it != load.end(); ++it)
535548
{
@@ -618,6 +631,12 @@ bool p3FileDatabase::loadList(std::list<RsItem *>& load)
618631
if(sscanf(kit->value.c_str(),"%d",&t) == 1)
619632
max_share_depth = (uint32_t)t ;
620633
}
634+
else if(kit->key == UPLOAD_STATS_RETENTION_DAYS_SS)
635+
{
636+
int t=0;
637+
if(sscanf(kit->value.c_str(),"%d",&t) == 1)
638+
mUploadStatsRetentionDays = t;
639+
}
621640

622641
delete *it ;
623642
continue ;
@@ -654,7 +673,28 @@ bool p3FileDatabase::loadList(std::list<RsItem *>& load)
654673

655674
if(fu)
656675
{
657-
mCumulativeUploaded.insert(fu->hash_stats.begin(), fu->hash_stats.end()) ;
676+
// Migration V1 (deprecated Feb 2026) -> V2: Set timestamp to now
677+
rstime_t now = time(NULL);
678+
RsDbg() << "UPLOADSTATS Migrating V1 stats (count: " << fu->hash_stats.size() << ") to V2";
679+
for(auto const& [hash, bytes] : fu->hash_stats)
680+
{
681+
TimeBasedUploadStat& stat = mCumulativeUploaded[hash];
682+
stat.total_bytes = bytes;
683+
stat.last_upload_ts = now;
684+
mCumulativeUploadedAll += bytes;
685+
}
686+
}
687+
688+
RsFileListsUploadStatsItemV2 *fu2 = dynamic_cast<RsFileListsUploadStatsItemV2*>(*it) ;
689+
690+
if(fu2)
691+
{
692+
RsDbg() << "UPLOADSTATS Loading V2 stats (count: " << fu2->hash_stats.size() << ")";
693+
for(auto const& it_item : fu2->hash_stats)
694+
{
695+
mCumulativeUploaded[it_item.first] = it_item.second ;
696+
mCumulativeUploadedAll += it_item.second.total_bytes ;
697+
}
658698
}
659699

660700
delete *it ;
@@ -1056,17 +1096,14 @@ uint64_t p3FileDatabase::getCumulativeUpload(const RsFileHash& hash) const
10561096
RS_STACK_MUTEX(mFLSMtx);
10571097
auto it = mCumulativeUploaded.find(hash);
10581098
if (it != mCumulativeUploaded.end())
1059-
return it->second;
1099+
return it->second.total_bytes;
10601100
return 0;
10611101
}
10621102

10631103
uint64_t p3FileDatabase::getCumulativeUploadAll() const
10641104
{
10651105
RS_STACK_MUTEX(mFLSMtx);
1066-
uint64_t total = 0;
1067-
for (auto it = mCumulativeUploaded.begin(); it != mCumulativeUploaded.end(); ++it)
1068-
total += it->second;
1069-
return total;
1106+
return mCumulativeUploadedAll;
10701107
}
10711108

10721109
uint64_t p3FileDatabase::getCumulativeUploadNum() const
@@ -1078,15 +1115,93 @@ uint64_t p3FileDatabase::getCumulativeUploadNum() const
10781115
void p3FileDatabase::addUploadStats(const RsFileHash& hash, uint64_t size)
10791116
{
10801117
RS_STACK_MUTEX(mFLSMtx);
1081-
mCumulativeUploaded[hash] += size;
1118+
TimeBasedUploadStat& stat = mCumulativeUploaded[hash];
1119+
stat.total_bytes += size;
1120+
stat.last_upload_ts = time(NULL);
1121+
mCumulativeUploadedAll += size;
1122+
1123+
// RsDbg() << "UPLOADSTATS add stats: " << hash << " + " << size << " bytes. Total: " << stat.total_bytes << " ts: " << stat.last_upload_ts;
10821124
IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_OFTEN);
10831125
}
10841126

10851127
void p3FileDatabase::clearUploadStats()
10861128
{
1129+
RS_STACK_MUTEX(mFLSMtx);
1130+
RsDbg() << "UPLOADSTATS clearing all stats";
10871131
mCumulativeUploaded.clear();
1132+
mCumulativeUploadedAll = 0;
1133+
}
1134+
1135+
void p3FileDatabase::cleanupUploadStats(int days)
1136+
{
1137+
RS_STACK_MUTEX(mFLSMtx);
1138+
rstime_t now = time(NULL);
1139+
rstime_t cutoff = now - (rstime_t)days * 24 * 3600;
1140+
uint32_t removed_count = 0;
1141+
1142+
// RsDbg() << "UPLOADSTATS cleanup stats (retention: " << days << " days)";
1143+
1144+
for (auto it = mCumulativeUploaded.begin(); it != mCumulativeUploaded.end(); )
1145+
{
1146+
bool expired = (days > 0) && (it->second.last_upload_ts < cutoff);
1147+
bool still_shared = false;
1148+
1149+
// Optional: check if file is still shared.
1150+
// We only do this check if it's not already expired, to save some time.
1151+
if (!expired)
1152+
{
1153+
RsFileHash real_hash;
1154+
DirectoryStorage::EntryIndex indx;
1155+
still_shared = mLocalSharedDirs->searchHash(it->first, real_hash, indx);
1156+
}
1157+
1158+
if (expired || !still_shared)
1159+
{
1160+
if (expired)
1161+
{
1162+
RsDbg() << "UPLOADSTATS removing expired stat: " << it->first << " (ts: " << it->second.last_upload_ts << ")";
1163+
}
1164+
else
1165+
{
1166+
RsDbg() << "UPLOADSTATS removing stat for unshared file: " << it->first;
1167+
}
1168+
1169+
mCumulativeUploadedAll -= it->second.total_bytes;
1170+
it = mCumulativeUploaded.erase(it);
1171+
removed_count++;
1172+
}
1173+
else
1174+
{
1175+
++it;
1176+
}
1177+
}
1178+
if (removed_count > 0)
1179+
{
1180+
RsDbg() << "UPLOADSTATS cleanup removed " << removed_count << " entries.";
1181+
IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_OFTEN);
1182+
}
1183+
}
1184+
1185+
void p3FileDatabase::setUploadStatsRetentionDays(int days)
1186+
{
1187+
if (mUploadStatsRetentionDays != days)
1188+
{
1189+
mUploadStatsRetentionDays = days;
1190+
RsDbg() << "UPLOADSTATS setting retention days to: " << days;
1191+
IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_OFTEN);
1192+
1193+
// Trigger cleanup immediately if days > 0
1194+
if (days > 0)
1195+
{
1196+
cleanupUploadStats(days);
1197+
}
1198+
}
10881199
}
10891200

1201+
int p3FileDatabase::getUploadStatsRetentionDays() const
1202+
{
1203+
return mUploadStatsRetentionDays;
1204+
}
10901205
bool p3FileDatabase::removeExtraFile(const RsFileHash& hash)
10911206
{
10921207
bool ret = false;

src/file_sharing/p3filelists.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "ft/ftsearch.h"
6363
#include "ft/ftextralist.h"
6464
#include "retroshare/rsfiles.h"
65+
#include "file_sharing/rsfilelistitems.h"
6566
#include "services/p3service.h"
6667
#include "util/rstime.h"
6768
#include "file_sharing/hash_cache.h"
@@ -177,6 +178,12 @@ class p3FileDatabase: public p3Service, public p3Config, public ftSearch //, pub
177178
virtual uint64_t getCumulativeUploadNum() const;
178179
virtual void addUploadStats(const RsFileHash& hash, uint64_t size);
179180
void clearUploadStats();
181+
void cleanupUploadStats(int days);
182+
183+
void setUploadStatsRetentionDays(int days);
184+
int getUploadStatsRetentionDays() const;
185+
186+
180187

181188
// interface for hash caching
182189

@@ -301,7 +308,12 @@ class p3FileDatabase: public p3Service, public p3Config, public ftSearch //, pub
301308
bool mBannedFileListNeedsUpdate;
302309
rstime_t mLastPrimaryBanListChangeTimeStamp;
303310

304-
std::map<RsFileHash, uint64_t> mCumulativeUploaded;
311+
/**
312+
* @brief Map of uploaded files statistics (bytes + timestamp)
313+
*/
314+
std::map<RsFileHash, TimeBasedUploadStat> mCumulativeUploaded;
315+
uint64_t mCumulativeUploadedAll;
316+
int mUploadStatsRetentionDays;
305317

306318
void locked_sendBanInfo(const RsPeerId& pid);
307319
void handleBannedFilesInfo(RsFileListsBannedHashesItem *item);

src/file_sharing/rsfilelistitems.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@
2121
******************************************************************************/
2222
#include "serialiser/rsbaseserial.h"
2323

24-
#include "serialiser/rstypeserializer.h"
24+
#include <serialiser/rstypeserializer.h>
2525

2626
#include "file_sharing/rsfilelistitems.h"
2727

28+
template<> void RsTypeSerializer::serial_process<TimeBasedUploadStat>(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx, TimeBasedUploadStat& v, const std::string& /*name*/)
29+
{
30+
serial_process(j, ctx, v.last_upload_ts, "last_upload_ts");
31+
serial_process(j, ctx, v.total_bytes, "total_bytes");
32+
}
33+
2834
void RsFileListsSyncRequestItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
2935
{
3036
RsTypeSerializer::serial_process (j,ctx,entry_hash,"entry_hash") ;
@@ -55,6 +61,11 @@ void RsFileListsUploadStatsItem::serial_process(RsGenericSerializer::SerializeJo
5561
RsTypeSerializer::serial_process(j,ctx,hash_stats,"hash_stats") ;
5662
}
5763

64+
void RsFileListsUploadStatsItemV2::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
65+
{
66+
RsTypeSerializer::serial_process(j,ctx,hash_stats,"hash_stats") ;
67+
}
68+
5869
RsItem *RsFileListsSerialiser::create_item(uint16_t service,uint8_t type) const
5970
{
6071
if(service != RS_SERVICE_TYPE_FILE_DATABASE)
@@ -67,6 +78,7 @@ RsItem *RsFileListsSerialiser::create_item(uint16_t service,uint8_t type) const
6778
case RS_PKT_SUBTYPE_FILELISTS_BANNED_HASHES_ITEM: return new RsFileListsBannedHashesItem();
6879
case RS_PKT_SUBTYPE_FILELISTS_BANNED_HASHES_CONFIG_ITEM: return new RsFileListsBannedHashesConfigItem();
6980
case RS_PKT_SUBTYPE_FILELISTS_UPLOAD_STATS_ITEM: return new RsFileListsUploadStatsItem();
81+
case RS_PKT_SUBTYPE_FILELISTS_UPLOAD_STATS_ITEM_V2: return new RsFileListsUploadStatsItemV2();
7082
default:
7183
return NULL ;
7284
}

src/file_sharing/rsfilelistitems.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const uint8_t RS_PKT_SUBTYPE_FILELISTS_CONFIG_ITEM = 0x03;
4141
const uint8_t RS_PKT_SUBTYPE_FILELISTS_BANNED_HASHES_ITEM = 0x04;
4242
const uint8_t RS_PKT_SUBTYPE_FILELISTS_BANNED_HASHES_CONFIG_ITEM = 0x05;
4343
const uint8_t RS_PKT_SUBTYPE_FILELISTS_UPLOAD_STATS_ITEM = 0x06;
44+
const uint8_t RS_PKT_SUBTYPE_FILELISTS_UPLOAD_STATS_ITEM_V2 = 0x07;
4445

4546
/*!
4647
* Base class for filelist sync items
@@ -128,6 +129,10 @@ class RsFileListsBannedHashesConfigItem: public RsFileListsItem
128129
std::map<RsFileHash,BannedFileEntry> primary_banned_files_list ;
129130
};
130131

132+
/**
133+
* @deprecated Deprecated on Feb 2026. Use RsFileListsUploadStatsItemV2 instead.
134+
* To be removed when no longer needed for migration.
135+
*/
131136
class RsFileListsUploadStatsItem: public RsFileListsItem
132137
{
133138
public:
@@ -139,6 +144,27 @@ class RsFileListsUploadStatsItem: public RsFileListsItem
139144
std::map<RsFileHash, uint64_t> hash_stats;
140145
};
141146

147+
struct TimeBasedUploadStat
148+
{
149+
TimeBasedUploadStat() : last_upload_ts(0), total_bytes(0) {}
150+
151+
rstime_t last_upload_ts;
152+
uint64_t total_bytes;
153+
154+
bool operator==(const TimeBasedUploadStat& r) const { return last_upload_ts == r.last_upload_ts && total_bytes == r.total_bytes; }
155+
};
156+
157+
class RsFileListsUploadStatsItemV2: public RsFileListsItem
158+
{
159+
public:
160+
RsFileListsUploadStatsItemV2() : RsFileListsItem(RS_PKT_SUBTYPE_FILELISTS_UPLOAD_STATS_ITEM_V2){}
161+
162+
virtual void clear() { hash_stats.clear(); }
163+
virtual void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx);
164+
165+
std::map<RsFileHash, TimeBasedUploadStat> hash_stats;
166+
};
167+
142168
class RsFileListsSerialiser : public RsServiceSerializer
143169
{
144170
public:

src/ft/ftserver.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2363,3 +2363,16 @@ void ftServer::clearUploadStats()
23632363
{
23642364
return mFileDatabase->clearUploadStats();
23652365
}
2366+
2367+
void ftServer::setUploadStatsRetentionDays(int days)
2368+
{
2369+
if (mFileDatabase)
2370+
mFileDatabase->setUploadStatsRetentionDays(days);
2371+
}
2372+
2373+
int ftServer::getUploadStatsRetentionDays()
2374+
{
2375+
if (mFileDatabase)
2376+
return mFileDatabase->getUploadStatsRetentionDays();
2377+
return 0;
2378+
}

src/ft/ftserver.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ class ftServer :
207207
virtual void setFilePermDirectDL(uint32_t perm) override;
208208
virtual uint32_t filePermDirectDL() override;
209209

210+
virtual void setUploadStatsRetentionDays(int days) override;
211+
virtual int getUploadStatsRetentionDays() override;
212+
210213
/// @see RsFiles
211214
std::error_condition requestFiles(
212215
const RsFileTree& collection,

src/retroshare/rsfiles.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,12 +792,24 @@ class RsFiles
792792
virtual void setFilePermDirectDL(uint32_t perm)=0;
793793

794794
/**
795-
* @brief Get Direct Download File Permission
795+
* @brief Set Direct Download File Permission
796796
* @jsonapi{development}
797797
* @return mFilePermDirectDLPolicy direct download permission
798798
*/
799799
virtual uint32_t filePermDirectDL()=0;
800800

801+
/**
802+
* @brief Set Upload Statistics Retention in Days
803+
* @param days Number of days to keep upload statistics (0 = keep forever)
804+
*/
805+
virtual void setUploadStatsRetentionDays(int days) = 0;
806+
807+
/**
808+
* @brief Get Upload Statistics Retention in Days
809+
* @return Number of days to keep upload statistics
810+
*/
811+
virtual int getUploadStatsRetentionDays() = 0;
812+
801813
/**
802814
* @brief Request remote files search
803815
* @jsonapi{development}

src/serialiser/rstlvkeys.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const uint32_t RSTLV_KEY_DISTRIB_ADMIN = 0x0040;
4343
const uint32_t RSTLV_KEY_DISTRIB_IDENTITY = 0x0080;
4444
const uint32_t RSTLV_KEY_DISTRIB_MASK = 0x00f0;
4545

46+
static const std::string UPLOAD_STATS_RETENTION_DAYS_SS = "UPLOAD_STATS_RETENTION_DAYS";
47+
4648
// Old class for RsTlvSecurityKey. Is kept for backward compatibility, but should not be serialised anymore
4749

4850
class RsTlvRSAKey: public RsTlvItem

0 commit comments

Comments
 (0)