AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.4.3
HybridPublishStore.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2024 60East Technologies Inc., All Rights Reserved.
4 //
5 // This computer software is owned by 60East Technologies Inc. and is
6 // protected by U.S. copyright laws and other laws and by international
7 // treaties. This computer software is furnished by 60East Technologies
8 // Inc. pursuant to a written license agreement and may be used, copied,
9 // transmitted, and stored only in accordance with the terms of such
10 // license agreement and with the inclusion of the above copyright notice.
11 // This computer software or any other copies thereof may not be provided
12 // or otherwise made available to any other person.
13 //
14 // U.S. Government Restricted Rights. This computer software: (a) was
15 // developed at private expense and is in all respects the proprietary
16 // information of 60East Technologies Inc.; (b) was not developed with
17 // government funds; (c) is a trade secret of 60East Technologies Inc.
18 // for all purposes of the Freedom of Information Act; and (d) is a
19 // commercial item and thus, pursuant to Section 12.212 of the Federal
20 // Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
21 // Government's use, duplication or disclosure of the computer software
22 // is subject to the restrictions set forth by 60East Technologies Inc..
23 //
25 
26 #ifndef _HYBRIDPUBLISHSTORE_H_
27 #define _HYBRIDPUBLISHSTORE_H_
28 
29 #include <amps/ampsplusplus.hpp>
31 #include <amps/PublishStore.hpp>
32 #if __cplusplus >= 201100L || _MSC_VER >= 1900
33  #include <atomic>
34 #endif
35 
40 
41 namespace AMPS
42 {
51  {
52  class HandlerData
53  {
54  public:
55  HybridPublishStore* _store;
57  void* _data;
58 
59  HandlerData()
60  : _store(NULL), _handler(NULL), _data(NULL)
61  { ; }
62 
63  void init(PublishStoreResizeHandler handler_, void* data_)
64  {
65  _handler = handler_;
66  _data = data_;
67  }
68  };
69 
70  class SwappingOutReplayer : public StoreReplayer
71  {
72  PublishStore* _pStore;
73 #if __cplusplus >= 201100L || _MSC_VER >= 1900
74  std::atomic<size_t> _entries;
75  std::atomic<size_t> _errorCount;
76  std::atomic<amps_uint64_t> _lastIndex;
77 #else
78  volatile size_t _entries;
79  volatile size_t _errorCount;
80  volatile amps_uint64_t _lastIndex;
81 #endif
82  public:
83  SwappingOutReplayer(PublishStore* pStore_, size_t entries_)
84  : _pStore(pStore_), _entries(entries_)
85  , _errorCount(0), _lastIndex(0)
86  { }
87 
88  size_t getErrors()
89  {
90  return _errorCount;
91  }
92 
93  amps_uint64_t lastIndex()
94  {
95  return _lastIndex;
96  }
97 
98  void execute(Message& message_)
99  {
100  if (_entries > 0 && _errorCount == 0)
101  {
102  try
103  {
104  {
105  _pStore->store(message_, false);
106  }
107  _lastIndex = amps_message_get_field_uint64(
108  message_.getMessage(),
109  AMPS_Sequence);
110  }
111  catch (...)
112  {
113  ++_errorCount;
114  }
115  --_entries;
116  }
117  }
118  };
119 
120  public:
131  HybridPublishStore(const char* fileName_, size_t maxMemoryCapacity_,
132  bool errorOnPublishGap_ = false)
133  : StoreImpl(errorOnPublishGap_)
134  , _memStore(maxMemoryCapacity_, errorOnPublishGap_)
135  , _fileStore(fileName_, errorOnPublishGap_)
136  , _cap(maxMemoryCapacity_)
137  , _lowWatermark((size_t)((double)maxMemoryCapacity_ * 0.5))
138  , _lowestIndexInMemory(0)
139  , _holdSwapping(false)
140  {
141  _handlerData._store = this;
142  _memStore.addRef();
143  _fileStore.addRef();
144  }
145 
156  HybridPublishStore(const std::string& fileName_, size_t maxMemoryCapacity_,
157  bool errorOnPublishGap_ = false)
158  : StoreImpl(errorOnPublishGap_)
159  , _memStore(maxMemoryCapacity_, errorOnPublishGap_)
160  , _fileStore(fileName_, errorOnPublishGap_)
161  , _cap(maxMemoryCapacity_)
162  , _lowWatermark((size_t)((double)maxMemoryCapacity_ * 0.5))
163  , _lowestIndexInMemory(0)
164  , _holdSwapping(false)
165  {
166  _handlerData._store = this;
167  _memStore.addRef();
168  _fileStore.addRef();
169  }
170 
177  void setLowWatermark(size_t lowWatermark_)
178  {
179  Lock<Mutex> guard(_lock);
180  _lowWatermark = lowWatermark_;
181  }
182 
190  {
191  Lock<Mutex> guard(_lock);
192  return _lowWatermark;
193  }
194 
198  void discardUpTo(amps_uint64_t index_)
199  {
200  Lock<Mutex> guard(_lock);
201  while (_holdSwapping)
202  {
203  if (!_lock.wait(1000))
204  {
205  Unlock<Mutex> u(_lock);
206  amps_invoke_waiting_function();
207  }
208  }
209  // Set _holdSwapping true to end of function
210  FlagFlip flip(&_holdSwapping);
211  {
212  Unlock<Mutex> u(_lock);
213  if (!index_)
214  {
215  _memStore.discardUpTo(_fileStore.getLastPersisted());
216  Lock<Mutex> l(_lock);
217  _lock.signalAll();
218  return;
219  }
220  _fileStore.discardUpTo(index_);
221  if (_lowestIndexInMemory <= index_)
222  {
223  _memStore.discardUpTo(index_);
224  _lowestIndexInMemory = index_ + 1;
225  }
226  }
227  _lock.signalAll();
228  }
229 
234  void replay(StoreReplayer& replayer_)
235  {
236  Lock<Mutex> guard(_lock);
237  while (_holdSwapping)
238  {
239  if (!_lock.wait(1000))
240  {
241  amps_invoke_waiting_function();
242  }
243  }
244  // Set _holdSwapping true to end of function
245  FlagFlip flip(&_holdSwapping);
246  {
247  Unlock<Mutex> u(_lock);
248  _fileStore.replay(replayer_);
249  _memStore.replay(replayer_);
250  }
251  _lock.signalAll();
252  }
253 
256  size_t unpersistedCount() const
257  {
258  return _fileStore.unpersistedCount() + _memStore.unpersistedCount();
259  }
260 
269  virtual void flush(long timeout_)
270  {
271  Lock<Mutex> guard(_lock);
272  amps_uint64_t waitFor = _getHybridHighestUnpersisted();
273  amps_uint64_t unset = getUnsetSequence();
274  // Check that we aren't already empty
275  if (waitFor == unset)
276  {
277  return;
278  }
279  if (timeout_ > 0)
280  {
281  bool timedOut = false;
282  long waitTime = (timeout_ < 1000) ? timeout_ : 1000;
283  AMPS_START_TIMER(timeout_)
284  // While timeout hasn't expired and we haven't had everything acked
285  while (!timedOut && waitFor >= _getHybridLowestUnpersisted() &&
286  _getHybridLowestUnpersisted() != unset)
287  {
288  if (!_lock.wait(waitTime))
289  {
290  // May have woken up early, check real time
291  AMPS_RESET_TIMER(timedOut, timeout_);
292  waitTime = (timeout_ < 1000) ? timeout_ : 1000;
293  Unlock<Mutex> unlck(_lock);
294  amps_invoke_waiting_function();
295  }
296  }
297  // If we timed out and still haven't caught up with the acks
298  if (timedOut && waitFor >= _getHybridLowestUnpersisted() &&
299  _getHybridLowestUnpersisted() != unset)
300  {
301  throw TimedOutException("Timed out waiting to flush publish store.");
302  }
303  }
304  else
305  {
306  while (waitFor >= _getHybridLowestUnpersisted() &&
307  _getHybridLowestUnpersisted() != unset)
308  {
309  // Use timeout version so python can interrupt
310  _lock.wait(1000);
311  Unlock<Mutex> unlck(_lock);
312  amps_invoke_waiting_function();
313  }
314  }
315  }
316 
317  bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
318  {
319  amps_uint64_t lowestIndexInMemory;
320  {
321  Lock<Mutex> guard(_lock);
322  lowestIndexInMemory = _lowestIndexInMemory;
323  }
324  if (index_ < lowestIndexInMemory)
325  {
326  return _fileStore.replaySingle(replayer_, index_);
327  }
328  else
329  {
330  return _memStore.replaySingle(replayer_, index_);
331  }
332  }
333 
337  amps_uint64_t store(const Message& message_)
338  {
339  Lock<Mutex> guard(_lock);
340  while (_holdSwapping)
341  {
342  if (!_lock.wait(1000))
343  {
344  Unlock<Mutex> u(_lock);
345  amps_invoke_waiting_function();
346  }
347  }
348  if (_memStore.unpersistedCount() >= _cap && !_holdSwapping)
349  {
350  // Set _holdSwapping true to end of function
351  FlagFlip flip(&_holdSwapping);
352  SwappingOutReplayer swapper(&_fileStore,
353  _memStore.unpersistedCount() - _lowWatermark);
354  {
355  Unlock<Mutex> u(_lock);
356  _memStore.replay(swapper);
357  }
358  _lock.signalAll();
359  if (swapper.getErrors() == 0)
360  {
361  _lowestIndexInMemory = swapper.lastIndex();
362  _memStore.discardUpTo(_lowestIndexInMemory++);
363  }
364  }
365  return _memStore.store(message_);
366  }
367 
368  void setResizeHandler(PublishStoreResizeHandler handler_, void* data_)
369  {
370  _handlerData.init(handler_, data_);
371  _fileStore.setResizeHandler(HybridPublishStore::resizeHandler,
372  (void*)&_handlerData);
373  }
374 
375  inline virtual PublishStoreResizeHandler getResizeHandler() const
376  {
377  return _handlerData._handler;
378  }
379 
380  amps_uint64_t getLowestUnpersisted() const
381  {
382  Lock<Mutex> guard(_lock);
383  return _getHybridLowestUnpersisted();
384  }
385 
386  amps_uint64_t getHighestUnpersisted() const
387  {
388  Lock<Mutex> guard(_lock);
389  return _getHybridHighestUnpersisted();
390  }
391 
392  amps_uint64_t getLastPersisted(void)
393  {
394  Lock<Mutex> guard(_lock);
395  return _getHybridLastPersisted();
396  }
397 
398  inline virtual void setErrorOnPublishGap(bool errorOnPublishGap_)
399  {
400  StoreImpl::setErrorOnPublishGap(errorOnPublishGap_);
401  _memStore.setErrorOnPublishGap(errorOnPublishGap_);
402  _fileStore.setErrorOnPublishGap(errorOnPublishGap_);
403  }
404 
405  private:
406 
407  // Resize handlers are invoked with Store not const Store&
408  static bool resizeHandler(Store store_, size_t size_, void* data_) // -V813
409  {
410  HandlerData* handlerData = (HandlerData*)data_;
411  //Unlock<Mutex> hybridUnlock(handlerData->_store->_lock);
412  return handlerData->_handler(store_, size_, handlerData->_data);
413  }
414 
415  // Lock should be held
416  amps_uint64_t _getHybridLowestUnpersisted() const
417  {
418  amps_uint64_t filemin = _fileStore.getLowestUnpersisted();
419  amps_uint64_t memmin = _memStore.getLowestUnpersisted();
420  if (filemin == AMPS_UNSET_SEQUENCE)
421  {
422  return memmin;
423  }
424  if (memmin == AMPS_UNSET_SEQUENCE || memmin > filemin)
425  {
426  return filemin;
427  }
428  // Only left with memmin <= filemin
429  return memmin;
430  }
431 
432  // Lock should be held
433  amps_uint64_t _getHybridHighestUnpersisted() const
434  {
435  amps_uint64_t filemax = _fileStore.getHighestUnpersisted();
436  amps_uint64_t memmax = _memStore.getHighestUnpersisted();
437  if (filemax == AMPS_UNSET_SEQUENCE)
438  {
439  return memmax;
440  }
441  if (memmax == AMPS_UNSET_SEQUENCE || memmax < filemax)
442  {
443  return filemax;
444  }
445  // Only left with memmax >= filemax
446  return memmax;
447  }
448 
449  amps_uint64_t _getHybridLastPersisted()
450  {
451  // If we've never swapped and nothing is in file
452  if (!_lowestIndexInMemory &&
453  _fileStore.unpersistedCount() == 0)
454  {
455  _fileStore.discardUpTo(_memStore.getLastPersisted());
456  return _fileStore.getLastPersisted();
457  }
458  amps_uint64_t memLast = _memStore.getLastPersisted();
459  amps_uint64_t fileLast = _fileStore.getLastPersisted();
460  return (memLast < fileLast) ? memLast : fileLast;
461  }
462 
463  MemoryPublishStore _memStore;
464  PublishStore _fileStore;
465  size_t _cap;
466  size_t _lowWatermark;
467  amps_uint64_t _lowestIndexInMemory;
468  mutable Mutex _lock;
469  HandlerData _handlerData;
470 #if __cplusplus >= 201100L || _MSC_VER >= 1900
471  std::atomic<bool> _holdSwapping;
472 #else
473  volatile bool _holdSwapping;
474 #endif
475 
476  };//end HybridPublishStore
477 
478 }//end namespace AMPS
479 
480 #endif //_HYBRIDPUBLISHSTORE_H_
481 
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:1086
HybridPublishStore(const char *fileName_, size_t maxMemoryCapacity_, bool errorOnPublishGap_=false)
Create a HybridPublishStore that will use fileName_ as its file storage and stores at most maxMemoryC...
Definition: HybridPublishStore.hpp:131
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:1058
AMPSDLL amps_uint64_t amps_message_get_field_uint64(amps_handle message, FieldId field)
Gets the unsigned 64-bit int value of a header field in an AMPS message.
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:531
HybridPublishStore(const std::string &fileName_, size_t maxMemoryCapacity_, bool errorOnPublishGap_=false)
Create a HybridPublishStore that will use fileName_ as its file storage and stores at most maxMemoryC...
Definition: HybridPublishStore.hpp:156
Provides AMPS::PublishStore, a publish store that uses memory-mapped files to provide a publish store...
void replay(StoreReplayer &replayer_)
Used internally by Client to replay messages in the store to AMPS after a successful connection...
Definition: HybridPublishStore.hpp:234
amps_uint64_t store(const Message &message_)
Used internally by Client to put messages into the Store.
Definition: HybridPublishStore.hpp:337
virtual void flush(long timeout_)
Method to wait for the Store to discard everything that has been stored up to the point in time when ...
Definition: HybridPublishStore.hpp:269
size_t unpersistedCount() const
The number of messages in the Store that have not been discarded.
Definition: HybridPublishStore.hpp:256
A StoreImpl implementation that uses a memory-mapped file to provide a publish store that persists ac...
Definition: PublishStore.hpp:46
amps_uint64_t getLowestUnpersisted() const
Get the oldest unpersisted message sequence in the store.
Definition: HybridPublishStore.hpp:380
Core type, function, and class declarations for the AMPS C++ client.
size_t getLowWatermark()
Get how many messags remain in memory after messages get offlined.
Definition: HybridPublishStore.hpp:189
void setLowWatermark(size_t lowWatermark_)
Set how many messags remain in memory after messages get offlined.
Definition: HybridPublishStore.hpp:177
bool(* PublishStoreResizeHandler)(Store store_, size_t size_, void *userData_)
Function type for PublishStore resize events The store_ param is store which is resizing.
Definition: ampsplusplus.hpp:1080
Provides AMPS::MemoryPublishStore, a publish store that holds messages in memory. ...
void setResizeHandler(PublishStoreResizeHandler handler_, void *data_)
Set a handler to be called if the Store needs to resize in order to keep storing messages.
Definition: HybridPublishStore.hpp:368
void discardUpTo(amps_uint64_t index_)
Discard all messages in the store up to and including index_.
Definition: HybridPublishStore.hpp:198
Handle class for StoreImpl classes that track publish messages.
Definition: ampsplusplus.hpp:1212
amps_uint64_t getLastPersisted(void)
Get the last persisted sequence number.
Definition: HybridPublishStore.hpp:392
virtual amps_uint64_t store(const Message &message_)
Store a given message that will be delivered to AMPS.
Definition: BlockPublishStore.hpp:251
bool replaySingle(StoreReplayer &replayer_, amps_uint64_t index_)
Called by Client to get a single message replayed by the store onto the StoreReplayer.
Definition: HybridPublishStore.hpp:317
Definition: ampsplusplus.hpp:102
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:1156
A StoreImpl implementation that uses MemoryStoreBuffer as its buffer to hold published messages in me...
Definition: MemoryPublishStore.hpp:44
An implementation of StoreImpl for publication.
Definition: HybridPublishStore.hpp:50