AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.3.1
HybridPublishStore.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2021 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 <ampsplusplus.hpp>
30 #include <MemoryPublishStore.hpp>
31 #include <PublishStore.hpp>
32 
37 
38 namespace AMPS
39 {
48 {
49  class HandlerData
50  {
51  public:
52  HybridPublishStore* _store;
54  void* _data;
55 
56  HandlerData()
57  : _store(NULL), _handler(NULL), _data(NULL)
58  { ; }
59 
60  void init(PublishStoreResizeHandler handler_, void* data_)
61  {
62  _handler = handler_;
63  _data = data_;
64  }
65  };
66 
67  class SwappingOutReplayer : public StoreReplayer
68  {
69  PublishStore* _pStore;
70  volatile size_t _entries, _errorCount;
71  volatile amps_uint64_t _lastIndex;
72  public:
73  SwappingOutReplayer(PublishStore* pStore_, size_t entries_)
74  : _pStore(pStore_), _entries(entries_)
75  , _errorCount(0), _lastIndex(0)
76  { }
77 
78  size_t getErrors() { return _errorCount; }
79 
80  amps_uint64_t lastIndex() { return _lastIndex; }
81 
82  void execute(Message& message_)
83  {
84  if(_entries > 0 && _errorCount == 0)
85  {
86  try
87  {
88  {
89  _pStore->store(message_, false);
90  }
91  _lastIndex = amps_message_get_field_uint64(message_.getMessage(),
92  AMPS_Sequence);
93  }
94  catch(...)
95  {
96  ++_errorCount;
97  }
98  --_entries;
99  }
100  }
101  };
102 
103 public:
111  HybridPublishStore(const char* fileName_, size_t maxMemoryCapacity_)
112  : StoreImpl(), _memStore(maxMemoryCapacity_), _fileStore(fileName_),
113  _cap(maxMemoryCapacity_),
114  _lowWatermark((size_t)((double)maxMemoryCapacity_ * 0.5)),
115  _lowestIndexInMemory(0), _holdSwapping(false)
116  {
117  _handlerData._store = this;
118  _memStore.addRef();
119  _fileStore.addRef();
120  }
121 
129  HybridPublishStore(const std::string& fileName_, size_t maxMemoryCapacity_)
130  : StoreImpl(), _memStore(maxMemoryCapacity_), _fileStore(fileName_),
131  _cap(maxMemoryCapacity_),
132  _lowWatermark((size_t)((double)maxMemoryCapacity_ * 0.5)),
133  _lowestIndexInMemory(0), _holdSwapping(false)
134  {
135  _handlerData._store = this;
136  _memStore.addRef();
137  _fileStore.addRef();
138  }
139 
146  void setLowWatermark(size_t lowWatermark_)
147  {
148  Lock<Mutex> guard(_lock);
149  _lowWatermark = lowWatermark_;
150  }
151 
159  {
160  Lock<Mutex> guard(_lock);
161  return _lowWatermark;
162  }
163 
167  void discardUpTo(amps_uint64_t index_)
168  {
169  Lock<Mutex> guard(_lock);
170  while (_holdSwapping)
171  {
172  Unlock<Mutex> u(_lock);
173  AMPS_YIELD();
174  }
175  // Set _holdSwapping true to end of function
176  FlagFlip flip(&_holdSwapping);
177  Unlock<Mutex> u(_lock);
178  if (!index_)
179  {
180  _memStore.discardUpTo(_fileStore.getLastPersisted());
181  return;
182  }
183  _fileStore.discardUpTo(index_);
184  if (_lowestIndexInMemory <= index_)
185  {
186  _memStore.discardUpTo(index_);
187  _lowestIndexInMemory = index_ + 1;
188  }
189  _lock.signalAll();
190  }
191 
196  void replay(StoreReplayer& replayer_)
197  {
198  Lock<Mutex> guard(_lock);
199  while (_holdSwapping)
200  {
201  Unlock<Mutex> u(_lock);
202  AMPS_YIELD();
203  }
204  // Set _holdSwapping true to end of function
205  FlagFlip flip(&_holdSwapping);
206  Unlock<Mutex> u(_lock);
207  _fileStore.replay(replayer_);
208  _memStore.replay(replayer_);
209  _lock.signalAll();
210  }
211 
214  size_t unpersistedCount() const
215  {
216  return _fileStore.unpersistedCount() + _memStore.unpersistedCount();
217  }
218 
227  virtual void flush(long timeout_)
228  {
229  Lock<Mutex> guard(_lock);
230  amps_uint64_t waitFor = _getHybridHighestUnpersisted();
231  amps_uint64_t unset = getUnsetSequence();
232  // Check that we aren't already empty
233  if (waitFor == unset) return;
234  if (timeout_ > 0)
235  {
236  bool timedOut = false;
237  AMPS_START_TIMER(timeout_)
238  // While timeout hasn't expired and we haven't had everything acked
239  while (!timedOut && waitFor >= _getHybridLowestUnpersisted() &&
240  _getHybridLowestUnpersisted() != unset)
241  {
242  if (!_lock.wait(timeout_))
243  {
244  // May have woken up early, check real time
245  AMPS_RESET_TIMER(timedOut, timeout_);
246  }
247  }
248  // If we timed out and still haven't caught up with the acks
249  if (timedOut && waitFor >= _getHybridLowestUnpersisted() &&
250  _getHybridLowestUnpersisted() != unset)
251  {
252  throw TimedOutException("Timed out waiting to flush publish store.");
253  }
254  }
255  else
256  {
257  while (waitFor >= _getHybridLowestUnpersisted() &&
258  _getHybridLowestUnpersisted() != unset)
259  {
260  // Use timeout version so python can interrupt
261  _lock.wait(1000);
262  Unlock<Mutex> unlck(_lock);
263  amps_invoke_waiting_function();
264  }
265  }
266  }
267 
268  bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
269  {
270  amps_uint64_t lowestIndexInMemory;
271  {
272  Lock<Mutex> guard(_lock);
273  lowestIndexInMemory = _lowestIndexInMemory;
274  }
275  if(index_ < lowestIndexInMemory)
276  {
277  return _fileStore.replaySingle(replayer_, index_);
278  }
279  else
280  {
281  return _memStore.replaySingle(replayer_, index_);
282  }
283  }
284 
288  amps_uint64_t store(const Message& message_)
289  {
290  Lock<Mutex> guard(_lock);
291  //while (_holdSwapping && _memStore.unpersistedCount() >= _cap)
292  while (_holdSwapping)
293  {
294  Unlock<Mutex> u(_lock);
295  AMPS_YIELD();
296  }
297  if(_memStore.unpersistedCount() >= _cap && !_holdSwapping)
298  {
299  // Set _holdSwapping true to end of function
300  FlagFlip flip(&_holdSwapping);
301  SwappingOutReplayer swapper(&_fileStore,
302  _memStore.unpersistedCount() - _lowWatermark);
303  {
304  Unlock<Mutex> u(_lock);
305  _memStore.replay(swapper);
306  }
307  if(swapper.getErrors() == 0)
308  {
309  _lowestIndexInMemory = swapper.lastIndex();
310  _memStore.discardUpTo(_lowestIndexInMemory++);
311  }
312  }
313  return _memStore.store(message_);
314  }
315 
316  void setResizeHandler(PublishStoreResizeHandler handler_, void* data_)
317  {
318  _handlerData.init(handler_, data_);
319  _fileStore.setResizeHandler(HybridPublishStore::resizeHandler,
320  (void*)&_handlerData);
321  }
322 
323  inline virtual PublishStoreResizeHandler getResizeHandler() const
324  {
325  return _handlerData._handler;
326  }
327 
328  amps_uint64_t getLowestUnpersisted() const
329  {
330  Lock<Mutex> guard(_lock);
331  return _getHybridLowestUnpersisted();
332  }
333 
334  amps_uint64_t getHighestUnpersisted() const
335  {
336  Lock<Mutex> guard(_lock);
337  return _getHybridHighestUnpersisted();
338  }
339 
340  amps_uint64_t getLastPersisted(void)
341  {
342  Lock<Mutex> guard(_lock);
343  return _getHybridLastPersisted();
344  }
345 
346 private:
347 
348  static bool resizeHandler(Store store_, size_t size_, void* data_)
349  {
350  HandlerData* handlerData = (HandlerData*)data_;
351  //Unlock<Mutex> hybridUnlock(handlerData->_store->_lock);
352  return handlerData->_handler(store_, size_, handlerData->_data);
353  }
354 
355  // Lock should be held
356  amps_uint64_t _getHybridLowestUnpersisted() const
357  {
358  amps_uint64_t filemin = _fileStore.getLowestUnpersisted();
359  amps_uint64_t memmin = _memStore.getLowestUnpersisted();
360  if (filemin == AMPS_UNSET_SEQUENCE)
361  return memmin;
362  if (memmin == AMPS_UNSET_SEQUENCE || memmin > filemin)
363  return filemin;
364  // Only left with memmin <= filemin
365  return memmin;
366  }
367 
368  // Lock should be held
369  amps_uint64_t _getHybridHighestUnpersisted() const
370  {
371  amps_uint64_t filemax = _fileStore.getHighestUnpersisted();
372  amps_uint64_t memmax = _memStore.getHighestUnpersisted();
373  if (filemax == AMPS_UNSET_SEQUENCE)
374  return memmax;
375  if (memmax == AMPS_UNSET_SEQUENCE || memmax < filemax)
376  return filemax;
377  // Only left with memmax >= filemax
378  return memmax;
379  }
380 
381  amps_uint64_t _getHybridLastPersisted()
382  {
383  // If we've never swapped and nothing is in file
384  if (!_lowestIndexInMemory &&
385  _fileStore.unpersistedCount() == 0)
386  {
387  _fileStore.discardUpTo(_memStore.getLastPersisted());
388  return _fileStore.getLastPersisted();
389  }
390  amps_uint64_t memLast = _memStore.getLastPersisted();
391  amps_uint64_t fileLast = _fileStore.getLastPersisted();
392  return (memLast < fileLast) ? memLast : fileLast;
393  }
394 
395  MemoryPublishStore _memStore;
396  PublishStore _fileStore;
397  size_t _cap;
398  size_t _lowWatermark;
399  amps_uint64_t _lowestIndexInMemory;
400  mutable Mutex _lock;
401  HandlerData _handlerData;
402  volatile bool _holdSwapping;
403 
404 };//end HybridPublishStore
405 
406 }//end namespace AMPS
407 
408 #endif //_HYBRIDPUBLISHSTORE_H_
409 
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:759
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:731
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:501
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:196
amps_uint64_t store(const Message &message_)
Used internally by Client to put messages into the Store.
Definition: HybridPublishStore.hpp:288
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:227
size_t unpersistedCount() const
The number of messages in the Store that have not been discarded.
Definition: HybridPublishStore.hpp:214
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:328
Core type, function, and class declarations for the AMPS C++ client.
HybridPublishStore(const char *fileName_, size_t maxMemoryCapacity_)
Create a HybridPublishStore that will use fileName_ as its file storage and stores at most maxMemoryC...
Definition: HybridPublishStore.hpp:111
size_t getLowWatermark()
Get how many messags remain in memory after messages get offlined.
Definition: HybridPublishStore.hpp:158
void setLowWatermark(size_t lowWatermark_)
Set how many messags remain in memory after messages get offlined.
Definition: HybridPublishStore.hpp:146
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:753
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:316
void discardUpTo(amps_uint64_t index_)
Discard all messages in the store up to and including index_.
Definition: HybridPublishStore.hpp:167
Handle class for StoreImpl classes that track publish messages.
Definition: ampsplusplus.hpp:857
amps_uint64_t getLastPersisted(void)
Get the last persisted sequence number.
Definition: HybridPublishStore.hpp:340
virtual amps_uint64_t store(const Message &message_)
Store a given message that will be delivered to AMPS.
Definition: BlockPublishStore.hpp:247
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:268
HybridPublishStore(const std::string &fileName_, size_t maxMemoryCapacity_)
Create a HybridPublishStore that will use fileName_ as its file storage and stores at most maxMemoryC...
Definition: HybridPublishStore.hpp:129
Definition: ampsplusplus.hpp:103
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:815
A StoreImpl implementation that uses MemoryStoreBuffer as its buffer to hold published messages in me...
Definition: MemoryPublishStore.hpp:44
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.
An implementation of StoreImpl for publication.
Definition: HybridPublishStore.hpp:47