@@ -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
10631103uint64_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
10721109uint64_t p3FileDatabase::getCumulativeUploadNum () const
@@ -1078,15 +1115,93 @@ uint64_t p3FileDatabase::getCumulativeUploadNum() const
10781115void 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
10851127void 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+ }
10901205bool p3FileDatabase::removeExtraFile (const RsFileHash& hash)
10911206{
10921207 bool ret = false ;
0 commit comments