@@ -262,12 +262,13 @@ int ChronicleMetaDirectory::acquire_story(chl::ClientId const& client_id,
262262 * chronolog::CL_ERR_NOT_EXIST if the Chronicle does not exist \n
263263 * chronolog::CL_ERR_UNKNOWN otherwise
264264 */
265- // TO_DO return acquisition_count after the story has been released
266265int ChronicleMetaDirectory::release_story (chl::ClientId const & client_id,
267266 const std::string& chronicle_name,
268267 const std::string& story_name,
269- StoryId& story_id)
268+ StoryId& story_id,
269+ bool & was_last_acquirer)
270270{
271+ was_last_acquirer = false ;
271272 LOG_DEBUG (" [ChronicleMetaDirectory] ClientID={} releasing StoryName={} in ChronicleName={}" ,
272273 client_id,
273274 story_name.c_str (),
@@ -294,8 +295,11 @@ int ChronicleMetaDirectory::release_story(chl::ClientId const& client_id,
294295 if (acquirerMapRecord != acquirerMap.end ())
295296 {
296297 /* All checks passed and entry found, manipulate metadata */
297- /* Decrement AcquisitionCount */
298- pStory->decrementAcquisitionCount ();
298+ /* Decrement AcquisitionCount; the post-decrement value tells the caller
299+ * whether other clients still hold the story. The recording group must
300+ * only be told to stop when the last acquirer releases. */
301+ uint64_t remaining_acquirers = pStory->decrementAcquisitionCount ();
302+ was_last_acquirer = (remaining_acquirers == 0 );
299303 story_id = pStory->getSid ();
300304 /* Remove this client from acquirerClientList of the Story */
301305 pStory->removeAcquirerClient (client_id);
@@ -317,6 +321,76 @@ int ChronicleMetaDirectory::release_story(chl::ClientId const& client_id,
317321 return ret;
318322}
319323
324+ int ChronicleMetaDirectory::release_all_acquired_stories (chl::ClientId const & client_id,
325+ std::vector<StoryId>& released_with_no_acquirers_left)
326+ {
327+ released_with_no_acquirers_left.clear ();
328+ if (clientRegistryManager_ == nullptr )
329+ {
330+ return chronolog::CL_ERR_UNKNOWN ;
331+ }
332+
333+ // Snapshot under the ClientRegistryManager's mutex; release_story below
334+ // mutates acquiredStoryList_ via the manager and an unsynchronized walk
335+ // here would race with concurrent Acquire/Release on this client.
336+ std::vector<std::pair<uint64_t , Story*>> snapshot;
337+ int snapshot_ret = clientRegistryManager_->get_acquired_stories_snapshot (client_id, snapshot);
338+ if (snapshot_ret == chronolog::CL_ERR_NOT_EXIST )
339+ {
340+ // Nothing to release if the client has no record (e.g. already disconnected).
341+ return chronolog::CL_SUCCESS ;
342+ }
343+ if (snapshot_ret != chronolog::CL_SUCCESS )
344+ {
345+ return snapshot_ret;
346+ }
347+ released_with_no_acquirers_left.reserve (snapshot.size ());
348+
349+ for (auto & [sid, pStory]: snapshot)
350+ {
351+ if (pStory == nullptr )
352+ {
353+ continue ;
354+ }
355+
356+ uint64_t cid = pStory->getCid ();
357+ std::string const story_name = pStory->getName ();
358+
359+ std::string chronicle_name;
360+ {
361+ std::lock_guard<std::mutex> chronicleMapLock (g_chronicleMetaDirectoryMutex_);
362+ auto chronicleRecord = chronicleMap_->find (cid);
363+ if (chronicleRecord == chronicleMap_->end ())
364+ {
365+ continue ;
366+ }
367+ chronicle_name = chronicleRecord->second ->getName ();
368+ }
369+
370+ StoryId released_id{0 };
371+ bool was_last_acquirer = false ;
372+ int ret = release_story (client_id, chronicle_name, story_name, released_id, was_last_acquirer);
373+ if (ret == chronolog::CL_SUCCESS )
374+ {
375+ // Only report ids whose last acquirer was this client. If other
376+ // clients still hold the story, the recording group must keep going.
377+ if (was_last_acquirer)
378+ {
379+ released_with_no_acquirers_left.push_back (released_id);
380+ }
381+ }
382+ else
383+ {
384+ LOG_WARNING (" [ChronicleMetaDirectory] Failed to auto-release StoryName={} for ClientID={}: rc={}" ,
385+ story_name.c_str (),
386+ client_id,
387+ ret);
388+ }
389+ }
390+
391+ return chronolog::CL_SUCCESS ;
392+ }
393+
320394int ChronicleMetaDirectory::get_chronicle_attr (std::string const & name, const std::string& key, std::string& value)
321395{
322396 LOG_DEBUG (" [ChronicleMetaDirectory] Getting attributes Key={} from ChronicleName={}" , key.c_str (), name.c_str ());
0 commit comments