[WIP] Avatar persistence and synchronization#248
[WIP] Avatar persistence and synchronization#248jolavillette wants to merge 8 commits intoRetroShare:masterfrom
Conversation
93fe7c5 to
f7273a3
Compare
src/chat/p3chatservice.cc
Outdated
|
|
||
| mChatMtx.lock(); /****** MUTEX LOCKED *******/ | ||
| /* 2. Original developer items (Messages & Status) */ | ||
| mChatMtx.lock(); |
There was a problem hiding this comment.
I don't like these .lock() (I know it was here before). It's probably a good opportunity to replace it with a stack mutex.
src/chat/p3chatservice.cc
Outdated
| ci->PeerId(mServiceCtrl->getOwnId()); | ||
| RsConfigKeyValueSet *item = new RsConfigKeyValueSet(); | ||
| RsTlvKeyValue kv; kv.key = "AV_CACHE:OWN"; | ||
| Radix64::encode(_own_avatar->_image_data, _own_avatar->_image_size, kv.value); |
There was a problem hiding this comment.
Note: RsConfigKeyValueSet is rather limiting in the type of what would be used (basically strings), which explains the Radix64 format. You would get a smaller print using an explicit encoding in the RsItem, but this will cause problems of backward compatibility.
src/chat/p3chatservice.cc
Outdated
| RsChatAvatarItem *p3ChatService::makeOwnAvatarItem() | ||
| { | ||
| RsStackMutex stack(mChatMtx); /********** STACK LOCKED MTX ******/ | ||
| /* Mutex is removed here to prevent recursive deadlock from parent calls */ |
There was a problem hiding this comment.
Good practice: if you remove the mutex, rename the method as locked_makeOwnAvatarItem() : this indicates that the method should be called within a mutex-protected region.
src/chat/p3chatservice.cc
Outdated
| RsDbg() << "AVATAR entering loadList, total items: " << (int)load.size(); | ||
|
|
||
| for(std::list<RsItem*>::const_iterator it(load.begin());it!=load.end();++it) | ||
| for(std::list<RsItem*>::iterator it(load.begin()); it != load.end(); ) |
There was a problem hiding this comment.
Since you changed the way your own avatar is saved, this is not backward compatible: patched users will lose their avatar and will have to re-define it.
I propose a better alternative: use RsChatAvatarItem as before, setting the PeerId() of the item to the node ID of that avatar. This is backward compatible with what's done now, and also will require less storage since it's not converting to Radix64.
src/chat/p3chatservice.cc
Outdated
| RsChatStatusItem *mitem = dynamic_cast<RsChatStatusItem *>(item); | ||
| if(mitem) | ||
| { | ||
| _custom_status_string = mitem->status_string ; |
|
Use RsChatAvatarItem instead of KeyValueSet => it will be backward compatible and the code is also simpler. |
|
Following Cyril comments:
|
ba0143c to
9a9813e
Compare
| } | ||
| _avatars[ci->PeerId()] = new AvatarInfo(ci->image_data,ci->image_size) ; | ||
| _avatars[ci->PeerId()]->_peer_is_new = true ; | ||
| _avatars[ci->PeerId()]->_own_is_new = new_peer ; |
There was a problem hiding this comment.
How do you keep track of peers to which we haven't sent our own avatar yet?
| uint32_t s = 0 ; | ||
| #ifdef CHAT_DEBUG | ||
| std::cerr << "p3chatservice:: own avatar requested from above. " << std::endl ; | ||
| //std::cerr << "p3chatservice:: own avatar requested from above. " << std::endl ; |
There was a problem hiding this comment.
I assume you left CHAT_DEBUG on. Make sure all debug output is disabled/cleaned before we merge.
| RsPeerId pid = ai->PeerId(); | ||
| if(pid.isNull() || pid == mServiceCtrl->getOwnId()) { | ||
| if(_own_avatar == NULL) _own_avatar = new AvatarInfo(ai->image_data, ai->image_size); | ||
| item_handled = true; |
There was a problem hiding this comment.
I don't understand the need for this item_handled mechanism. In the end, all items that have been loaded need to be destroyed (unless one was actually stored. Does this happen? We didn't have this check before).
There was a problem hiding this comment.
ah I see. Each step would delete the item separately. So it's fine as it is now.
| } | ||
|
|
||
| if(kv != NULL) { | ||
| if(!kv->tlvkvs.pairs.empty()) |
There was a problem hiding this comment.
this check is not necessary (following the code above it's not possible to have an item with an empty list). It's of course not a problem to keep it.
src/chat/p3chatservice.cc
Outdated
|
|
||
| /* AVATAR Handshake on connection */ | ||
| RsDbg() << "AVATAR peer connected, initiating sync with: " << it->id.toStdString().c_str(); | ||
| sendAvatarRequest(it->id); |
There was a problem hiding this comment.
This is going to cause sending the avatar at every connection. To make it reliable we should send the TS of the avatar we have, and if the peer has a more recent avatar then it should send it. Looking at the code in sendAvatarRequest() it should only be used if we do nto have the avatar.
src/chat/p3chatservice.cc
Outdated
| sendAvatarRequest(it->id); | ||
| if(_own_avatar != nullptr && _own_avatar->_image_size > 0) | ||
| { | ||
| sendAvatarJpegData(it->id); |
There was a problem hiding this comment.
same here. Why would we send our own avatar without a request?
|
0242ad7 to
5bece81
Compare
|
Save/restore of avatars is broken since last commit (TS). Stand by for a fix. |
…t missing avatar on peer connection for backward compatibility
Fix Avatar Persistence RegressionFixes avatar persistence issue introduced by timestamp sync implementation. Problem: Avatars were not persisting across sessions because Solution:
Result: Avatars now persist correctly and work with both old and new nodes. |
Code and comment by Gemini
This summary describes the technical implementation of the new avatar persistence and synchronization system for RetroShare's Chat Service.
Core Implementation Overview
The goal of these changes was to provide a reliable, persistent, and proactive avatar exchange mechanism that operates silently in the background without interrupting the user experience or breaking existing services like Chat Lobbies.
To ensure avatars survive application restarts, the storage logic was migrated to a Key-Value set system within the configuration.
saveList: Now encodes the local node's avatar and all cached peer avatars into RsConfigKeyValueSet items using Radix64 encoding. It explicitly calls parent services to ensure Chat Lobby subscriptions are also saved.
loadList: Implements a non-destructive "Pass-through" logic. It extracts avatar data from KV sets but relays any unrecognized items to DistributedChatService::processLoadListItem and DistantChatService::processLoadListItem.
Lobby Protection: This delegation ensures that shared configuration containers containing lobby data or GXS identities are not deleted by the Chat Service during the loading phase.
The system now proactively ensures that avatars are up-to-date across the network through two main triggers:
Broadcast on Update (setOwnNodeAvatarData): When the user changes their avatar, the service immediately broadcasts the new image data to all currently online peers.
Handshake on Connection (statusChange): As soon as a peer connects, a bidirectional handshake is initiated. The service sends an avatarRequest to the peer and simultaneously pushes the local avatar data to them.
Silent Protocol: These exchanges use direct RsChatAvatarItem packets, which are processed by the remote UI in the background, preventing unnecessary chat window popups.
A critical deadlock identified through GDB debugging was resolved to ensure system stability.
Deadlock Prevention: The RsStackMutex was removed from makeOwnAvatarItem to prevent recursive locking when called from parent functions that already hold the mChatMtx lock.
Scoped Locking: In setOwnNodeAvatarData, the mutex is now confined to a specific scope to ensure it is released before entering the network broadcast loop, allowing child functions to acquire the lock safely.