-
Notifications
You must be signed in to change notification settings - Fork 38
[WIP] Avatar persistence and synchronization #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Avatar persistence and synchronization #248
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove the mutex?
|
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you left CHAT_DEBUG on. Make sure all debug output is disabled/cleaned before we merge.
src/chat/p3chatservice.cc
Outdated
| } | ||
|
|
||
| if(kv != NULL) { | ||
| if(!kv->tlvkvs.pairs.empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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. |
…t missing avatar on peer connection for backward compatibility
5608ae9 to
e1fee08
Compare
csoler
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the new requested change, but I really think it would be for the best.
src/chat/p3chatservice.cc
Outdated
| /* Fix: Constructor for combined TS + Radix64 loading */ | ||
| AvatarInfo(const std::string& encoded_data) : _image_size(0), _image_data(NULL) | ||
| { | ||
| if (encoded_data.length() > 16) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way of doing things is pretty dangerous: you may get a full radix64 string that unfortunately starts with digits and therefore interpret it the wrong way.
All this is because of the key/value storage in SaveList(). I would be much simpler/safer to use a RsItem instead. I suggest that you create a new RsItem type (say RsAvatarInfoConfigItem) that includes the TS of the avatar, the owner of the avatar, and the avatar as RsTlvImage: this way no need for specific serialization (the serializer does it automatically).
You can either keep that structure as the holder of the info (after loading it) meaning that in this case you do not destroy the avatar item that you just got in LoadList, or convert it to an AvatarInfo structure as done currently.
Since this new avatar item type can be used for all peers including yourself, it makes the saving more consistent. In LoadList, just keep the loading for the original avatar item for backward compatibility.
DescriptionThis commit refactors the avatar persistence mechanism in [p3ChatService] to address safety concerns regarding manual string parsing. Changes
Verification
|
src/chat/p3chatservice.cc
Outdated
| } | ||
|
|
||
| /* Fix: Returns TS (16 hex chars) + Radix64 image data */ | ||
| std::string toRadix64() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is not used anymore now, so you can remove it.
src/chat/p3chatservice.cc
Outdated
| ~AvatarInfo() { if (_image_data) free(_image_data); } | ||
|
|
||
| /* Fix: Constructor for combined TS + Radix64 loading */ | ||
| AvatarInfo(const std::string& encoded_data) : _image_size(0), _image_data(NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor is not used anymore: remove it.
src/chat/p3chatservice.cc
Outdated
| _avatars[pid] = new AvatarInfo(mit->value); | ||
| if (_avatars.count(pid)) delete _avatars[pid]; | ||
|
|
||
| /* Backward compatibility: decode KVS string (TS prefix + Radix64) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can also remove that part: missing avatars will be transferred anyway, and this code is a security hole.
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.