reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
InMemorySyncPubDb.cxx
Go to the documentation of this file.
2#include "rutil/compat.hxx"
3#include "rutil/Timer.hxx"
4#include "rutil/Logger.hxx"
6
7using namespace resip;
8
9#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
10
11InMemorySyncPubDb::InMemorySyncPubDb(bool syncEnabled) : mSyncEnabled(syncEnabled)
12{
13}
14
15void
17{
18 Lock lock(mHandlerMutex);
19 mHandlers.push_back(handler);
20}
21
22void
24{
25 Lock lock(mHandlerMutex);
26 for(HandlerList::iterator it = mHandlers.begin(); it != mHandlers.end(); it++)
27 {
28 if(*it == handler)
29 {
30 mHandlers.erase(it);
31 break;
32 }
33 }
34}
35
36bool
38{
39 if (mSyncEnabled)
40 {
41 // Check if already lingering
42 if (document.mExpirationTime == 0)
43 {
44 // Check if linger time is done
45 if (document.mLingerTime <= now)
46 {
47 return true;
48 }
49 }
50 else if (document.mExpirationTime <= now)
51 {
52 // Tag document to linger
53 document.mLastUpdated = document.mExpirationTime;
54 document.mExpirationTime = 0;
55 }
56 }
57 else
58 {
59 if (document.mExpirationTime <= now)
60 {
61 return true;
62 }
63 }
64 return false;
65}
66
67void
68InMemorySyncPubDb::initialSync(unsigned int connectionId)
69{
71
73
74 // Iterate through keys
75 KeyToETagMap::iterator keyIt = mPublicationDb.begin();
76 for (; keyIt != mPublicationDb.end(); )
77 {
78 // Iterator through documents in sub-map
79 ETagToDocumentMap::iterator eTagIt = keyIt->second.begin();
80 for (; eTagIt != keyIt->second.end();)
81 {
82 if (shouldEraseDocument(eTagIt->second, now))
83 {
84 keyIt->second.erase(eTagIt++);
85 }
86 else
87 {
88 invokeOnInitialSyncDocument(connectionId, eTagIt->second.mEventType, eTagIt->second.mDocumentKey, eTagIt->second.mETag, eTagIt->second.mExpirationTime, eTagIt->second.mLastUpdated, eTagIt->second.mContents.get(), eTagIt->second.mSecurityAttributes.get());
89 eTagIt++;
90 }
91 }
92
93 // If there are no more eTags then remove entity
94 if (keyIt->second.empty())
95 {
96 mPublicationDb.erase(keyIt++);
97 }
98 else
99 {
100 keyIt++;
101 }
102 }
103}
104
105void
107{
108 Data mapKey = document.mEventType + document.mDocumentKey;
109 bool found = false;
110
112 KeyToETagMap::iterator keyIt = mPublicationDb.find(mapKey);
113 if (keyIt != mPublicationDb.end())
114 {
115 // Next find eTag in sub-map
116 ETagToDocumentMap::iterator eTagIt = keyIt->second.find(document.mETag);
117 if (eTagIt != keyIt->second.end())
118 {
119 // Doc was found! Do some checks
120 found = true;
121 // If doc is from sync then ensure it is newer
122 if (!document.mSyncPublication || (document.mLastUpdated > eTagIt->second.mLastUpdated))
123 {
125 auto contentsForOnDocumentModified = document.mContents;
126 auto securityAttributesForOnDocumentModified = document.mSecurityAttributes;
127 // We should only need to linger a document past the latest expiration time we have ever seen, since both sides will
128 // treat the publication as gone after this time anyway. However this is timing sensitive with the sync process.
129 // So we will linger a document for twice this duration.
130 uint64_t lingerDuration = (resipMax(document.mExpirationTime, eTagIt->second.mExpirationTime) - now) * 2;
131 if (document.mContents.get() == 0) // If this is a pub refresh then ensure we don't get rid of existing doc body
132 {
133 // If previous document was expired then ensure we push out a notify on the refresh to tell everyone it's back
134 // This can happen if someone deletes a publication on the web page, then it is refreshed. The delete causes a
135 // notify of closed state, the refresh should bring the state back.
136 if (eTagIt->second.mExpirationTime == 0 ||
137 eTagIt->second.mExpirationTime < now)
138 {
139 contentsForOnDocumentModified = eTagIt->second.mContents;
140 securityAttributesForOnDocumentModified = eTagIt->second.mSecurityAttributes;
141 }
142 const auto contents = eTagIt->second.mContents;
143 const auto securityAttributes = eTagIt->second.mSecurityAttributes;
144 eTagIt->second = document;
145 eTagIt->second.mContents = contents;
146 eTagIt->second.mSecurityAttributes = securityAttributes;
147 }
148 else
149 {
150 eTagIt->second = document;
151 }
152 eTagIt->second.mLingerTime = now + lingerDuration;
153 // Only pass sync as true if this update just came from an inbound sync operation
154 invokeOnDocumentModified(document.mSyncPublication /* sync publication? */, document.mEventType, document.mDocumentKey, document.mETag, document.mExpirationTime, document.mLastUpdated, contentsForOnDocumentModified.get(), securityAttributesForOnDocumentModified.get());
155 }
156 }
157 }
158
159 // If we didn't find an existing document and we have a contents, then add this doc.
160 // Note: Pub refreshes don't contain a contents - so we happen to receive a refresh as our
161 // first message for an etag we don't want to add it to the store - until we have
162 // at least a doc body.
163 if (!found && document.mContents)
164 {
165 // Add new
166 mPublicationDb[mapKey][document.mETag] = document;
167 // Only pass sync as true if this update just came from an inbound sync operation
168 invokeOnDocumentModified(document.mSyncPublication /* sync publication? */, document.mEventType, document.mDocumentKey, document.mETag, document.mExpirationTime, document.mLastUpdated, document.mContents.get(), document.mSecurityAttributes.get());
169 }
170}
171
172bool
173InMemorySyncPubDb::removeDocument(const Data& eventType, const Data& documentKey, const Data& eTag, uint64_t lastUpdated, bool syncPublication)
174{
175 bool result = false;
177
178 // First find entity in map
179 KeyToETagMap::iterator keyIt = mPublicationDb.find(eventType + documentKey);
180 if (keyIt != mPublicationDb.end())
181 {
182 // Next find eTag in sub-map
183 ETagToDocumentMap::iterator eTagIt = keyIt->second.find(eTag);
184 if (eTagIt != keyIt->second.end())
185 {
186 result = true;
187 // If remove is from sync then ensure it is newer
188 if (!syncPublication || (lastUpdated > eTagIt->second.mLastUpdated))
189 {
190 // If sync is enabled - then linger the record in memory until it expires
191 if (mSyncEnabled)
192 {
193 // Tag document as expired, but in a linger state
194 eTagIt->second.mExpirationTime = 0;
195 eTagIt->second.mLastUpdated = Timer::getTimeSecs();
196 }
197 else
198 {
199 // ETag was found - remove it
200 keyIt->second.erase(eTagIt);
201 }
202 // Only pass sync as true if this update just come from an inbound sync operation
203 invokeOnDocumentRemoved(syncPublication /* sync? */, eventType, documentKey, eTag, lastUpdated);
204 }
205 }
206 // If there are no more eTags then remove entity
207 if (keyIt->second.empty())
208 {
209 mPublicationDb.erase(keyIt);
210 }
211 }
212 return result;
213}
214
215bool
216InMemorySyncPubDb::getMergedETags(const Data& eventType, const Data& documentKey, ETagMerger& merger, Contents* destination)
217{
219
221
222 // Find entity
223 KeyToETagMap::iterator keyIt = mPublicationDb.find(eventType + documentKey);
224 if (keyIt != mPublicationDb.end())
225 {
226 bool isFirst = true;
227
228 // Iterate through all Etags
229 ETagToDocumentMap::iterator eTagIt = keyIt->second.begin();
230 for (; eTagIt != keyIt->second.end(); )
231 {
232 if (!shouldEraseDocument(eTagIt->second, now))
233 {
234 // Just because we don't need to erase it doesn't mean it didn't expire - check for expiration
235 if (eTagIt->second.mExpirationTime > now && eTagIt->second.mContents)
236 {
237 merger.mergeETag(destination, eTagIt->second.mContents.get(), isFirst);
238 isFirst = false;
239 }
240 eTagIt++;
241 }
242 else
243 {
244 // ETag has expired - remove it
245 keyIt->second.erase(eTagIt++);
246 // If no more Etags for key, then remove key entry and bail out
247 if (keyIt->second.empty())
248 {
249 mPublicationDb.erase(keyIt);
250 break;
251 }
252 }
253 }
254 // If we have at least on ETag then return true
255 if (!isFirst)
256 {
257 return true;
258 }
259 }
260 return false;
261}
262
263bool
264InMemorySyncPubDb::documentExists(const Data& eventType, const Data& documentKey, const Data& eTag)
265{
267
268 // First find entity in map
269 KeyToETagMap::iterator keyIt = mPublicationDb.find(eventType + documentKey);
270 if (keyIt != mPublicationDb.end())
271 {
272 // Next find eTag in sub-map
273 ETagToDocumentMap::iterator eTagIt = keyIt->second.find(eTag);
274 if (eTagIt != keyIt->second.end())
275 {
276 // Decided not to check if expired or not. Not checking allows us to handle
277 // a scenario where the publication refresh went to another repro node and
278 // syncing was broken for some reason. Then a new publish comes here and
279 // the record is still lingering.
280 //if (eTagIt->second.mExpirationTime <= Timer::getTimeSecs())
281 {
282 return true;
283 }
284 }
285 }
286 return false;
287}
288
289// If lastUpdated != 0 then we make sure that passed in lastUpdated matches the document before returning true
290// This method is used in timer expiry and the lastUpdated checks helps us to make sure the timer that just
291// expired hasn't been made obsolete due to a new update.
292bool InMemorySyncPubDb::checkExpired(const Data& eventType, const Data& documentKey, const Data& eTag, uint64_t lastUpdated)
293{
295
297
298 // First find entity in map
299 KeyToETagMap::iterator keyIt = mPublicationDb.find(eventType + documentKey);
300 if (keyIt != mPublicationDb.end())
301 {
302 // Next find eTag in sub-map
303 ETagToDocumentMap::iterator eTagIt = keyIt->second.find(eTag);
304 if (eTagIt != keyIt->second.end())
305 {
306 if (eTagIt->second.mExpirationTime >= now &&
307 (lastUpdated == 0 || lastUpdated == eTagIt->second.mLastUpdated))
308 {
309 DebugLog(<< "InMemorySyncPubDb::checkExpired: found expired publication, docKey=" << documentKey << ", tag=" << eTag);
310 bool syncPublication = eTagIt->second.mSyncPublication;
311 // If sync is enabled - then linger the record in memory until it expires
312 if (mSyncEnabled)
313 {
314 // Tag document as expired, but in a linger state
315 eTagIt->second.mExpirationTime = 0;
316 eTagIt->second.mLastUpdated = now;
317 }
318 else
319 {
320 // ETag was found - remove it
321 keyIt->second.erase(eTagIt);
322 // If no more Etags for key, then remove key entry
323 if (keyIt->second.empty())
324 {
325 mPublicationDb.erase(keyIt);
326 }
327 }
328 invokeOnDocumentRemoved(syncPublication /* sync? */, eventType, documentKey, eTag, now);
329 return true;
330 }
331 }
332 }
333 return false;
334}
335
336void
341
347
348void
353
354void
355InMemorySyncPubDb::invokeOnDocumentModified(bool sync, const Data& eventType, const Data& documentKey, const Data& eTag, uint64_t expirationTime, uint64_t lastUpdated, const Contents* contents, const SecurityAttributes* securityAttributes)
356{
357 Lock lock(mHandlerMutex);
358 for (HandlerList::iterator it = mHandlers.begin(); it != mHandlers.end(); it++)
359 {
360 // If handler mode is all, then send notification, otherwise handler mode is sync and we ensure passed
361 // in syncPublication flag is false - so we don't sync back to originator
362 if (!sync || (*it)->getMode() == InMemorySyncPubDbHandler::AllChanges)
363 {
364 (*it)->onDocumentModified(sync, eventType, documentKey, eTag, expirationTime, lastUpdated, contents, securityAttributes);
365 }
366 }
367}
368
369void
370InMemorySyncPubDb::invokeOnDocumentRemoved(bool sync, const Data& eventType, const Data& documentKey, const Data& eTag, uint64_t lastUpdated)
371{
372 Lock lock(mHandlerMutex);
373 for (HandlerList::iterator it = mHandlers.begin(); it != mHandlers.end(); it++)
374 {
375 // If handler mode is all, then send notification, otherwise handler mode is sync and we ensure passed
376 // in syncPublication flag is false - so we don't sync back to originator
377 if (!sync || (*it)->getMode() == InMemorySyncPubDbHandler::AllChanges)
378 {
379 (*it)->onDocumentRemoved(sync, eventType, documentKey, eTag, lastUpdated);
380 }
381 }
382}
383
384void
385InMemorySyncPubDb::invokeOnInitialSyncDocument(unsigned int connectionId, const Data& eventType, const Data& documentKey, const Data& eTag, uint64_t expirationTime, uint64_t lastUpdated, const Contents* contents, const SecurityAttributes* securityAttributes)
386{
387 Lock lock(mHandlerMutex);
388 for (HandlerList::iterator it = mHandlers.begin(); it != mHandlers.end(); it++)
389 {
390 // If handler mode is all, then send notification, otherwise handler mode is sync and we check the passed
391 // in sync flag
392 if ((*it)->getMode() == InMemorySyncPubDbHandler::SyncServer)
393 {
394 (*it)->onInitialSyncDocument(connectionId, eventType, documentKey, eTag, expirationTime, lastUpdated, contents, securityAttributes);
395 }
396 }
397}
398
399
400/* ====================================================================
401*
402* Copyright (c) 2015 SIP Spectrum, Inc. All rights reserved.
403*
404* Redistribution and use in source and binary forms, with or without
405* modification, are permitted provided that the following conditions
406* are met:
407*
408* 1. Redistributions of source code must retain the above copyright
409* notice, this list of conditions and the following disclaimer.
410*
411* 2. Redistributions in binary form must reproduce the above copyright
412* notice, this list of conditions and the following disclaimer in
413* the documentation and/or other materials provided with the
414* distribution.
415*
416* 3. Neither the name of the author(s) nor the names of any contributors
417* may be used to endorse or promote products derived from this software
418* without specific prior written permission.
419*
420* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND
421* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
422* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
423* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
424* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
425* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
426* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
427* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
428* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
429* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
430* SUCH DAMAGE.
431*
432* ====================================================================
433*
434*/
435/*
436* vi: set shiftwidth=3 expandtab:
437*/
#define DebugLog(args_)
Definition Logger.hxx:128
Base class for all SIP body types.
Definition Contents.hxx:51
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
virtual KeyToETagMap & getDocuments()
bool shouldEraseDocument(PubDocument &document, uint64_t now)
void invokeOnDocumentRemoved(bool sync, const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t lastUpdated)
void invokeOnInitialSyncDocument(unsigned int connectionId, const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t expirationTime, uint64_t lastUpdated, const Contents *contents, const SecurityAttributes *securityAttributes)
virtual bool checkExpired(const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t lastUpdated)
virtual bool documentExists(const Data &eventType, const Data &documentKey, const Data &eTag)
virtual void addUpdateDocument(const PubDocument &document)
virtual void initialSync(unsigned int connectionId)
void invokeOnDocumentModified(bool sync, const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t expirationTime, uint64_t lastUpdated, const Contents *contents, const SecurityAttributes *securityAttributes)
virtual void addHandler(InMemorySyncPubDbHandler *handler)
virtual bool removeDocument(const Data &eventType, const Data &documentKey, const Data &eTag, uint64_t lastUpdated, bool syncPublication=false)
InMemorySyncPubDb(bool syncEnabled=false)
virtual void removeHandler(InMemorySyncPubDbHandler *handler)
virtual bool getMergedETags(const Data &eventType, const Data &documentKey, ETagMerger &merger, Contents *destination)
virtual bool mergeETag(Contents *eTagDest, Contents *eTagSrc, bool isFirst)=0
std::map< resip::Data, ETagToDocumentMap > KeyToETagMap
static uint64_t getTimeSecs()
Returns the current clock time in seconds.
Definition Timer.hxx:78
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
dcm! – add UnusedChecking(_enum) below;
std::unique_lock< Mutex > Lock
A convenience class to lock a Lockable (such as a Mutex) on object creation, and unlock on destructio...
Definition Lock.hxx:28
unsigned long long uint64_t
Definition Stun.hxx:39
const _Tp & resipMax(const _Tp &__a, const _Tp &__b)
Definition compat.hxx:194