AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.0.5
HybridPublishStore.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2020 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  amps_invoke_waiting_function();
263  }
264  }
265  }
266 
267  bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
268  {
269  amps_uint64_t lowestIndexInMemory;
270  {
271  Lock<Mutex> guard(_lock);
272  lowestIndexInMemory = _lowestIndexInMemory;
273  }
274  if(index_ < lowestIndexInMemory)
275  {
276  return _fileStore.replaySingle(replayer_, index_);
277  }
278  else
279  {
280  return _memStore.replaySingle(replayer_, index_);
281  }
282  }
283 
287  amps_uint64_t store(const Message& message_)
288  {
289  Lock<Mutex> guard(_lock);
290  //while (_holdSwapping && _memStore.unpersistedCount() >= _cap)
291  while (_holdSwapping)
292  {
293  Unlock<Mutex> u(_lock);
294  AMPS_YIELD();
295  }
296  if(_memStore.unpersistedCount() >= _cap && !_holdSwapping)
297  {
298  // Set _holdSwapping true to end of function
299  FlagFlip flip(&_holdSwapping);
300  SwappingOutReplayer swapper(&_fileStore,
301  _memStore.unpersistedCount() - _lowWatermark);
302  {
303  Unlock<Mutex> u(_lock);
304  _memStore.replay(swapper);
305  }
306  if(swapper.getErrors() == 0)
307  {
308  _lowestIndexInMemory = swapper.lastIndex();
309  _memStore.discardUpTo(_lowestIndexInMemory++);
310  }
311  }
312  return _memStore.store(message_);
313  }
314 
315  void setResizeHandler(PublishStoreResizeHandler handler_, void* data_)
316  {
317  _handlerData.init(handler_, data_);
318  _fileStore.setResizeHandler(HybridPublishStore::resizeHandler,
319  (void*)&_handlerData);
320  }
321 
322  inline virtual PublishStoreResizeHandler getResizeHandler() const
323  {
324  return _handlerData._handler;
325  }
326 
327  amps_uint64_t getLowestUnpersisted() const
328  {
329  Lock<Mutex> guard(_lock);
330  return _getHybridLowestUnpersisted();
331  }
332 
333  amps_uint64_t getHighestUnpersisted() const
334  {
335  Lock<Mutex> guard(_lock);
336  return _getHybridHighestUnpersisted();
337  }
338 
339  amps_uint64_t getLastPersisted(void)
340  {
341  Lock<Mutex> guard(_lock);
342  return _getHybridLastPersisted();
343  }
344 
345 private:
346 
347  static bool resizeHandler(Store store_, size_t size_, void* data_)
348  {
349  HandlerData* handlerData = (HandlerData*)data_;
350  //Unlock<Mutex> hybridUnlock(handlerData->_store->_lock);
351  return handlerData->_handler(store_, size_, handlerData->_data);
352  }
353 
354  // Lock should be held
355  amps_uint64_t _getHybridLowestUnpersisted() const
356  {
357  amps_uint64_t filemin = _fileStore.getLowestUnpersisted();
358  amps_uint64_t memmin = _memStore.getLowestUnpersisted();
359  if (filemin == AMPS_UNSET_SEQUENCE)
360  return memmin;
361  if (memmin == AMPS_UNSET_SEQUENCE || memmin > filemin)
362  return filemin;
363  // Only left with memmin <= filemin
364  return memmin;
365  }
366 
367  // Lock should be held
368  amps_uint64_t _getHybridHighestUnpersisted() const
369  {
370  amps_uint64_t filemax = _fileStore.getHighestUnpersisted();
371  amps_uint64_t memmax = _memStore.getHighestUnpersisted();
372  if (filemax == AMPS_UNSET_SEQUENCE)
373  return memmax;
374  if (memmax == AMPS_UNSET_SEQUENCE || memmax < filemax)
375  return filemax;
376  // Only left with memmax >= filemax
377  return memmax;
378  }
379 
380  amps_uint64_t _getHybridLastPersisted()
381  {
382  // If we've never swapped and nothing is in file
383  if (!_lowestIndexInMemory &&
384  _fileStore.unpersistedCount() == 0)
385  {
386  _fileStore.discardUpTo(_memStore.getLastPersisted());
387  return _fileStore.getLastPersisted();
388  }
389  amps_uint64_t memLast = _memStore.getLastPersisted();
390  amps_uint64_t fileLast = _fileStore.getLastPersisted();
391  return (memLast < fileLast) ? memLast : fileLast;
392  }
393 
394  MemoryPublishStore _memStore;
395  PublishStore _fileStore;
396  size_t _cap;
397  size_t _lowWatermark;
398  amps_uint64_t _lowestIndexInMemory;
399  mutable Mutex _lock;
400  HandlerData _handlerData;
401  volatile bool _holdSwapping;
402 
403 };//end HybridPublishStore
404 
405 }//end namespace AMPS
406 
407 #endif //_HYBRIDPUBLISHSTORE_H_
408 
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:1143
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:1115
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:393
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:287
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:327
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:1137
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:315
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:1241
amps_uint64_t getLastPersisted(void)
Get the last persisted sequence number.
Definition: HybridPublishStore.hpp:339
virtual amps_uint64_t store(const Message &message_)
Store a given message that will be delivered to AMPS.
Definition: BlockPublishStore.hpp:245
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:267
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:136
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:1199
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