AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.5.17
BlockPublishStore.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2026 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 _BLOCKPUBLISHSTORE_H_
27 #define _BLOCKPUBLISHSTORE_H_
28 #include <amps/ampsplusplus.hpp>
29 #include <amps/BlockStore.hpp>
30 #include <amps/Buffer.hpp>
31 #include <sstream>
32 #include <stack>
33 #include <string>
34 #include <map>
35 #include <amps/ampscrc.hpp>
36 #if __cplusplus >= 201103L || _MSC_VER >= 1900
37  #include <atomic>
38 #endif
39 
40 #ifdef _WIN32
41  #include <intrin.h>
42  #include <sys/timeb.h>
43 #else
44  #include <sys/time.h>
45 #endif
46 #include <iostream>
47 
52 
53 namespace AMPS
54 {
60  class BlockPublishStore : public StoreImpl // -V553
61  {
62  public:
63  typedef BlockStore::Block Block;
64  typedef Lock<BlockStore> BufferLock;
65  typedef Unlock<BlockStore> BufferUnlock;
66 
67  typedef enum
68  {
69  SOW_DELETE_DATA = 0x01,
70  SOW_DELETE_FILTER = 0x02,
71  SOW_DELETE_KEYS = 0x04,
72  SOW_DELETE_BOOKMARK = 0x08,
73  SOW_DELETE_BOOKMARK_CANCEL = 0x10,
74  SOW_DELETE_UNKNOWN = 0x80
75  } SowDeleteType;
76 
79  enum Constants : amps_uint32_t
80  {
81  DEFAULT_BLOCK_HEADER_SIZE = 32,
82  DEFAULT_BLOCK_CHAIN_HEADER_SIZE = 64,
83  DEFAULT_BLOCKS_PER_REALLOC = 1000,
84  DEFAULT_BLOCK_SIZE = 2048
85  };
86 
87  /**********************************************************************
88  * Storage format
89  *************************************************************************
90  * Field Description | Type | # Bytes
91  *************************************************************************
92  * HEADER as detailed below | | 32 TOTAL
93  * | |
94  * Total number of blocks used by the record | uint32_t | 4
95  * Total length of the saved record | uint32_t | 4
96  * HA Message sequence | uint64_t | 8
97  * CRC Value - only set in first Block | uint64_t | 8
98  * next in chain offset | uint64_t | 8
99  *************************************************************************
100  * CHAIN HEADER as detailed below | | 64 TOTAL
101  * | |
102  * operation | uint32_t | 4
103  * command id length | uint32_t | 4
104  * correltation id length | uint32_t | 4
105  * expiration length | uint32_t | 4
106  * sow key length | uint32_t | 4
107  * topic length | uint32_t | 4
108  * sow delete flag | int32_t | 4
109  * ack types | uint32_t | 4
110  * unused [8] | uint32_t | 4*8 = 32
111  *************************************************************************
112  * DATA SECTION - can be spread across multiple blocks
113  *
114  * command id | char[]
115  * correlation id | char[]
116  * expiration | char[]
117  * sow key | char[]
118  * topic | char[]
119  * data | char[]
120  *************************************************************************/
121 
122  struct BlockHeader
123  {
124  amps_uint32_t _blocksToWrite;
125  amps_uint32_t _totalRemaining;
126  amps_uint64_t _seq;
127  amps_uint64_t _crcVal;
128  amps_uint64_t _nextInChain;
129  };
130 
131  struct BlockChainHeader
132  {
133  amps_uint32_t _operation;
134  amps_uint32_t _commandIdLen;
135  amps_uint32_t _correlationIdLen;
136  amps_uint32_t _expirationLen;
137  amps_uint32_t _sowKeyLen;
138  amps_uint32_t _topicLen;
139  amps_int32_t _flag;
140  amps_uint32_t _ackTypes;
141  amps_uint32_t _unused[8];
142  BlockChainHeader() // -V730
143  : _operation(0), _commandIdLen(0), _correlationIdLen(0)
144  , _expirationLen(0), _sowKeyLen(0), _topicLen(0), _flag(-1)
145  , _ackTypes(0)
146  { ; }
147  };
148 
153  static inline amps_uint32_t getBlockHeaderSize()
154  {
155  return DEFAULT_BLOCK_HEADER_SIZE;
156  }
157 
163  static inline amps_uint32_t getBlockChainHeaderSize()
164  {
165  return DEFAULT_BLOCK_CHAIN_HEADER_SIZE;
166  }
167 
171  inline amps_uint32_t getBlockSize()
172  {
173  return _blockStore.getBlockSize();
174  }
175 
179  inline amps_uint32_t getBlockDataSize()
180  {
181  return _blockStore.getBlockSize() - getBlockHeaderSize();
182  }
183 
201  amps_uint32_t blocksPerRealloc_ = 1000,
202  bool isFile_ = false,
203  bool errorOnPublishGap_ = false,
204  amps_uint32_t blockSize_ = DEFAULT_BLOCK_SIZE)
205  : StoreImpl(errorOnPublishGap_)
206  , _blockStore(buffer_, normalizeBlocksPerRealloc(blocksPerRealloc_, blockSize_),
207  DEFAULT_BLOCK_HEADER_SIZE,
208  normalizeBlockSize(blocksPerRealloc_, blockSize_))
209  , _metadataBlock(0)
210  , _maxDiscarded((amps_uint64_t)0), _lastSequence((amps_uint64_t)1)
211  , _stored(0)
212  {
213  _blockStore.setResizeHandler(&BlockPublishStore::canResize, (void*)this);
214  chooseCRC(isFile_);
215  if (!isFile_)
216  {
217  // This gets set in recover in file-based stores
218  BufferLock bufferGuard(_blockStore);
219  _blockStore.init();
220  _metadataBlock = _blockStore.get(1);
221  // Remove metadata block from used list
222  _blockStore.setUsedList(0);
223  _blockStore.setEndOfUsedList(0);
224  // Metadata block holds block size, block header size,
225  // last discarded sequence, client version
226  _metadataBlock->_sequence = (amps_uint64_t)0;
227  Buffer* pBuffer = _blockStore.getBuffer();
228  pBuffer->setPosition(_metadataBlock->_offset);
229  pBuffer->putUint32((amps_uint32_t)getBlockSize());
230  pBuffer->putUint32((amps_uint32_t)getBlockHeaderSize());
231  pBuffer->putUint64((amps_uint64_t)0);
232  // Metadata blocks puts client version in CRC position
233  pBuffer->putUint64((amps_uint64_t)VersionInfo::parseVersion(AMPS_CLIENT_VERSION));
234  // No next in chain
235  pBuffer->putUint64((amps_uint64_t)0);
236  }
237  }
238 
242  {
243  }
244 
251  virtual amps_uint64_t store(const Message& message_)
252  {
253  return store(message_, true);
254  }
255 
266  amps_uint64_t store(const Message& message_, bool assignSequence_)
267  {
268  const char* commandId, *correlationId, *expiration, *sowKey,
269  *topic, *data;
270  size_t dataLen = 0;
271  BlockHeader blockHeader;
272  BlockChainHeader chainHeader;
273  message_.getRawCommandId(&commandId, &dataLen);
274  chainHeader._commandIdLen = (amps_uint32_t)dataLen;
275  message_.getRawCorrelationId(&correlationId, &dataLen);
276  chainHeader._correlationIdLen = (amps_uint32_t)dataLen;
277  message_.getRawExpiration(&expiration, &dataLen);
278  chainHeader._expirationLen = (amps_uint32_t)dataLen;
279  message_.getRawSowKey(&sowKey, &dataLen);
280  chainHeader._sowKeyLen = (amps_uint32_t)dataLen;
281  message_.getRawTopic(&topic, &dataLen);
282  chainHeader._topicLen = (amps_uint32_t)dataLen;
283  message_.getRawData(&data, &dataLen);
284  chainHeader._flag = -1;
285  Message::Command::Type operation = message_.getCommandEnum();
286  chainHeader._operation = (amps_uint32_t)operation;
287  if (operation == Message::Command::SOWDelete)
288  {
289  if (dataLen > 0)
290  {
291  chainHeader._flag = SOW_DELETE_DATA;
292  }
293  else
294  {
295  message_.getRawFilter(&data, &dataLen);
296  if (dataLen > 0)
297  {
298  chainHeader._flag = SOW_DELETE_FILTER;
299  }
300  else
301  {
302  message_.getRawSowKeys(&data, &dataLen);
303  if (dataLen > 0)
304  {
305  chainHeader._flag = SOW_DELETE_KEYS;
306  }
307  else
308  {
309  message_.getRawBookmark(&data, &dataLen);
310  chainHeader._flag = SOW_DELETE_BOOKMARK;
311  // Check options for cancel
312  Message::Field options = message_.getOptions();
313  size_t remaining = options.len();
314  const void* next = NULL;
315  const void* start = (const void*)(options.data());
316  // Not necessarily null-terminated so no strstr
317  while (remaining >= 6 &&
318  (next = memchr(start, (int)'c', remaining)) != NULL)
319  {
320  remaining = (size_t)next - (size_t)start;
321  if (remaining >= 6 && strncmp((const char*)start,
322  "cancel", 6) == 0)
323  {
324  chainHeader._flag = SOW_DELETE_BOOKMARK_CANCEL;
325  break;
326  }
327  }
328  }
329  }
330  }
331  }
332  blockHeader._totalRemaining = (
333  (chainHeader._operation == Message::Command::Unknown)
334  ? 0
336  + chainHeader._commandIdLen
337  + chainHeader._correlationIdLen
338  + chainHeader._expirationLen
339  + chainHeader._sowKeyLen
340  + chainHeader._topicLen
341  + (amps_uint32_t)dataLen));
342  size_t lastBlockLength = ((operation == Message::Command::Unknown) ? 0 :
343  (blockHeader._totalRemaining % getBlockDataSize()));
344  blockHeader._blocksToWrite = ((operation == Message::Command::Unknown)
345  ? 1
346  : ((amps_uint32_t)(blockHeader._totalRemaining
347  / getBlockDataSize())
348  + ((lastBlockLength > 0) ? 1 : 0)));
349  blockHeader._crcVal = (amps_uint64_t)0ULL;
350  blockHeader._crcVal = _crc(commandId,
351  chainHeader._commandIdLen,
352  blockHeader._crcVal);
353  blockHeader._crcVal = _crc(correlationId,
354  chainHeader._correlationIdLen,
355  blockHeader._crcVal);
356  blockHeader._crcVal = _crc(expiration,
357  chainHeader._expirationLen,
358  blockHeader._crcVal);
359  blockHeader._crcVal = _crc(sowKey,
360  chainHeader._sowKeyLen,
361  blockHeader._crcVal);
362  blockHeader._crcVal = _crc(topic,
363  chainHeader._topicLen,
364  blockHeader._crcVal);
365  blockHeader._crcVal = _crc(data, dataLen, blockHeader._crcVal);
366 
367  // Reserve slots for storage, growing if necessary
368  BufferLock bufferGuard(_blockStore);
369  Block* first = _blockStore.get(blockHeader._blocksToWrite);
370  if (assignSequence_)
371  {
372  if (_lastSequence <= 2)
373  {
374  _getLastPersisted();
375  }
376  blockHeader._seq = ++_lastSequence;
377  }
378  else
379  {
380  blockHeader._seq = amps_message_get_field_uint64(
381  message_.getMessage(),
382  AMPS_Sequence);
383  if (!_maxDiscarded)
384  {
385  _maxDiscarded = blockHeader._seq - 1;
386  }
387  if (blockHeader._seq >= _lastSequence)
388  {
389  _lastSequence = blockHeader._seq;
390  }
391  }
392 
393  try
394  {
395  size_t topicWritten = 0UL;
396  size_t dataWritten = 0UL;
397  size_t commandWritten = 0UL;
398  size_t correlationWritten = 0UL;
399  size_t expirationWritten = 0UL;
400  size_t sowKeyWritten = 0UL;
401  Buffer* pBuffer = _blockStore.getBuffer();
402  for (Block* next = first; next; next = next->_nextInChain)
403  {
404  next->_sequence = blockHeader._seq;
405  if (next->_nextInChain)
406  {
407  blockHeader._nextInChain = next->_nextInChain->_offset;
408  }
409  else
410  {
411  blockHeader._nextInChain = (amps_uint64_t)0;
412  }
413  // Set buffer to start of Block and write the header
414  pBuffer->setPosition(next->_offset);
415  pBuffer->putBytes((const char*)&blockHeader, sizeof(BlockHeader));
416  // Clear crcVal, as it's only written in the first Block
417  blockHeader._crcVal = (amps_uint64_t)0;
418  size_t bytesRemaining = getBlockDataSize();
419  if (next == first)
420  {
421  // Write Block chain header
422  chainHeader._ackTypes = (amps_uint32_t)message_.getAckTypeEnum();
423  pBuffer->putBytes((const char*)&chainHeader,
424  sizeof(BlockChainHeader));
425  pBuffer->setPosition(next->_offset + getBlockHeaderSize() + getBlockChainHeaderSize());
426  bytesRemaining -= getBlockChainHeaderSize();
427  }
428  else
429  {
430  pBuffer->setPosition(next->_offset + getBlockHeaderSize());
431  }
432 
433  if (commandWritten < chainHeader._commandIdLen)
434  {
435  size_t commandWrite = (chainHeader._commandIdLen - commandWritten < bytesRemaining) ? chainHeader._commandIdLen - commandWritten : bytesRemaining;
436  pBuffer->putBytes(commandId + commandWritten,
437  commandWrite);
438  bytesRemaining -= commandWrite;
439  commandWritten += commandWrite;
440  }
441  if (correlationWritten < chainHeader._correlationIdLen)
442  {
443  size_t correlationWrite = (chainHeader._correlationIdLen - correlationWritten < bytesRemaining) ? chainHeader._correlationIdLen - correlationWritten : bytesRemaining;
444  pBuffer->putBytes(correlationId + correlationWritten,
445  correlationWrite);
446  bytesRemaining -= correlationWrite;
447  correlationWritten += correlationWrite;
448  }
449  if (bytesRemaining > 0 && expirationWritten < chainHeader._expirationLen)
450  {
451  size_t expWrite = (chainHeader._expirationLen - expirationWritten < bytesRemaining) ? chainHeader._expirationLen - expirationWritten : bytesRemaining;
452  pBuffer->putBytes(expiration + expirationWritten, expWrite);
453  bytesRemaining -= expWrite;
454  expirationWritten += expWrite;
455  }
456  if (bytesRemaining > 0 && sowKeyWritten < chainHeader._sowKeyLen)
457  {
458  size_t sowKeyWrite = (chainHeader._sowKeyLen - sowKeyWritten < bytesRemaining) ? chainHeader._sowKeyLen - sowKeyWritten : bytesRemaining;
459  pBuffer->putBytes(sowKey + sowKeyWritten, sowKeyWrite);
460  bytesRemaining -= sowKeyWrite;
461  sowKeyWritten += sowKeyWrite;
462  }
463  if (bytesRemaining > 0 && topicWritten < chainHeader._topicLen)
464  {
465  size_t topicWrite = (chainHeader._topicLen - topicWritten
466  < bytesRemaining)
467  ? chainHeader._topicLen - topicWritten
468  : bytesRemaining;
469  pBuffer->putBytes(topic + topicWritten, topicWrite);
470  bytesRemaining -= topicWrite;
471  topicWritten += topicWrite;
472  }
473  if (bytesRemaining > 0 && dataWritten < dataLen)
474  {
475  size_t dataWrite = (dataLen - dataWritten < bytesRemaining) ?
476  dataLen - dataWritten : bytesRemaining;
477  pBuffer->putBytes(data + dataWritten, dataWrite);
478  bytesRemaining -= dataWrite;
479  dataWritten += dataWrite;
480  }
481  }
482  }
483  catch (const AMPSException&)
484  {
485  _blockStore.put(first);
486  throw;
487  }
488  AMPS_FETCH_ADD(&_stored, 1);
489  return blockHeader._seq;
490  }
491 
498  virtual void discardUpTo(amps_uint64_t index_)
499  {
500  // Get the lock
501  BufferLock bufferGuard(_blockStore);
502  Buffer* pBuffer = _blockStore.getBuffer();
503  // Don't use _getLastPersisted() here, don't want to set it
504  // to something other than index_ if it's not already set
505  amps_uint64_t lastPersisted = _metadataBlock->_sequence;
506  // Make sure it's a real index and we have messages to discard
507  if (index_ == (amps_uint64_t)0 || !_blockStore.front() || index_ <= _maxDiscarded)
508  {
509  // During logon it's very possible we don't have a last persisted
510  // but that the Client is calling discardUpTo with the ack value.
511  if (lastPersisted < index_)
512  {
513  pBuffer->setPosition(_metadataBlock->_offset + 8);
514  pBuffer->putUint64(index_);
515  _metadataBlock->_sequence = index_;
516  if (_maxDiscarded < index_)
517  {
518  _maxDiscarded = index_;
519  }
520  if (_lastSequence <= index_)
521  {
522  _lastSequence = index_;
523  }
524  }
525  else if (!index_) // Fresh logon, no sequence history
526  {
527  _getLastPersisted();
528  }
529  else if (getErrorOnPublishGap() && index_ < lastPersisted) //Message gap
530  {
531  std::ostringstream os;
532  os << "Server last saw " << index_ << " from Client but Store "
533  << "has already discarded up to " << lastPersisted
534  << " which would leave a gap of unreceived messages.";
535  throw PublishStoreGapException(os.str());
536  }
537  _blockStore.signalAll();
538  return;
539  }
540 
541  _maxDiscarded = index_;
542  AMPS_FETCH_SUB(&_stored, _blockStore.put(index_));
543  _blockStore.signalAll();
544  if (lastPersisted >= index_)
545  {
546  return;
547  }
548  pBuffer->setPosition(_metadataBlock->_offset + 8);
549  pBuffer->putUint64(index_);
550  _metadataBlock->_sequence = index_;
551  if (_lastSequence < index_)
552  {
553  _lastSequence = index_;
554  }
555  }
556 
561  void replay(StoreReplayer& replayer_)
562  {
563  // Get the lock
564  BufferLock bufferGuard(_blockStore);
565  // If we don't have anything yet, return
566  if (!_blockStore.front())
567  {
568  return;
569  }
570  Block* next = _blockStore.front();
571  try
572  {
573  for (Block* block = _blockStore.front(); block; block = next)
574  {
575  // Replay the message
576  replayOnto(block, replayer_);
577  next = block->_nextInList;
578  }
579  }
580  catch (const StoreException&)
581  {
582  _blockStore.putAll(next);
583  throw;
584  }
585  }
586 
593  bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
594  {
595  BufferLock bufferGuard(_blockStore);
596  // If we don't have anything yet, return
597  if (!_blockStore.front())
598  {
599  return false;
600  }
601  // Get the end point
602  amps_uint64_t lastIdx = _blockStore.back()->_sequence;
603  // Get the start point
604  amps_uint64_t leastIdx = _blockStore.front()->_sequence;
605  if (index_ >= leastIdx && index_ <= lastIdx)
606  {
607  Block* block = _blockStore.front();
608  while (block && block->_sequence != index_)
609  {
610  block = block->_nextInList;
611  }
612  if (!block)
613  {
614  return false;
615  }
616  // If total bytes is 0, it's a queue ack and gets skipped.
617  Buffer* pBuffer = _blockStore.getBuffer();
618  pBuffer->setPosition(block->_offset +
619  sizeof(amps_uint32_t));
620  if (pBuffer->getUint32() == 0)
621  {
622  return false;
623  }
624  replayOnto(block, replayer_);
625  return true;
626  }
627  else // Get Store and Client back in sync
628  {
629  _message.reset();
630  leastIdx -= 1;
631  _message.setSequence(leastIdx);
632  replayer_.execute(_message);
633  return false;
634  }
635  }
636 
642  size_t unpersistedCount() const
643  {
644  size_t count = (size_t)_stored;
645  return count;
646  }
647 
656  virtual void flush(long timeout_)
657  {
658  BufferLock bufferGuard(_blockStore);
659  amps_uint64_t waitFor = _getHighestUnpersisted();
660  // Check that we aren't already empty
661  if (waitFor == getUnsetSequence())
662  {
663  return;
664  }
665  if (timeout_ > 0)
666  {
667  bool timedOut = false;
668  AMPS_START_TIMER(timeout_);
669  // While timeout hasn't expired and we haven't had everything acked
670  while (!timedOut && _stored != 0
671  && waitFor >= _getLowestUnpersisted())
672  {
673  if (!_blockStore.wait(timeout_))
674  {
675  // May have woken up early, check real time
676  AMPS_RESET_TIMER(timedOut, timeout_);
677  }
678  }
679  // If we timed out and still haven't caught up with the acks
680  if (timedOut && _stored != 0
681  && waitFor >= _getLowestUnpersisted())
682  {
683  throw TimedOutException("Timed out waiting to flush publish store.");
684  }
685  }
686  else
687  {
688  while (_stored != 0 && waitFor >= _getLowestUnpersisted())
689  {
690  // Still wake up every 1s so python can interrupt
691  _blockStore.wait(1000);
692  // Don't hold lock if possibly grabbing GIL
693  BufferUnlock unlck(_blockStore);
694  amps_invoke_waiting_function();
695  }
696  }
697  }
698 
699  amps_uint64_t getLowestUnpersisted() const
700  {
701  BufferLock bufferGuard(_blockStore);
702  return _getLowestUnpersisted();
703  }
704 
705  amps_uint64_t getHighestUnpersisted() const
706  {
707  BufferLock bufferGuard(_blockStore);
708  return _getHighestUnpersisted();
709  }
710 
711  amps_uint64_t getLastPersisted(void)
712  {
713  BufferLock bufferGuard(_blockStore);
714  return _getLastPersisted();
715  }
716 
717  protected:
718  static size_t normalizeBlockCountValue(size_t blocksPerRealloc_)
719  {
720  if (blocksPerRealloc_ == 0)
721  {
722  return DEFAULT_BLOCKS_PER_REALLOC;
723  }
724  if (blocksPerRealloc_ > BlockStore::MAX_BLOCKS_PER_REALLOC)
725  {
726  return BlockStore::MAX_BLOCKS_PER_REALLOC;
727  }
728  return blocksPerRealloc_;
729  }
730 
731  static amps_uint32_t normalizeBlockSizeValue(amps_uint32_t blockSize_)
732  {
733  if (blockSize_ == 0)
734  {
735  return DEFAULT_BLOCK_SIZE;
736  }
737  if (blockSize_ < DEFAULT_BLOCK_CHAIN_HEADER_SIZE * 2)
738  {
739  return DEFAULT_BLOCK_CHAIN_HEADER_SIZE * 2;
740  }
741  return blockSize_ & ~127U; // multiple of 128
742  }
743 
744  static amps_uint32_t normalizeBlocksPerRealloc(size_t blocksPerRealloc_,
745  amps_uint32_t blockSize_)
746  {
747  size_t blocks = normalizeBlockCountValue(blocksPerRealloc_);
748  amps_uint32_t blockSize = normalizeBlockSizeValue(blockSize_);
749 
750  if (blocks > (std::numeric_limits<size_t>::max)() / blockSize)
751  {
752  return DEFAULT_BLOCKS_PER_REALLOC;
753  }
754  return (amps_uint32_t)blocks;
755  }
756 
757  static amps_uint32_t normalizeBlockSize(size_t blocksPerRealloc_,
758  amps_uint32_t blockSize_)
759  {
760  size_t blocks = normalizeBlockCountValue(blocksPerRealloc_);
761  amps_uint32_t blockSize = normalizeBlockSizeValue(blockSize_);
762 
763  if (blocks > (std::numeric_limits<size_t>::max)() / blockSize)
764  {
765  return DEFAULT_BLOCK_SIZE;
766  }
767  return blockSize;
768  }
769 
770  static size_t getInitialBufferSize(size_t blocksPerRealloc_,
771  amps_uint32_t blockSize_)
772  {
773  return (size_t)normalizeBlocksPerRealloc(blocksPerRealloc_, blockSize_)
774  * normalizeBlockSize(blocksPerRealloc_, blockSize_);
775  }
776 
777  static bool canResize(size_t requestedSize_, void* vpThis_)
778  {
779  BlockPublishStore* me = (BlockPublishStore*)vpThis_;
780  return me->callResizeHandler(requestedSize_);
781  }
782 
783  amps_uint64_t _getLowestUnpersisted() const
784  {
785  // Assume the lock is held
786  // If we don't have anything, return MAX
787  if (!_blockStore.front())
788  {
789  return getUnsetSequence();
790  }
791  return _blockStore.front()->_sequence;
792  }
793 
794  amps_uint64_t _getHighestUnpersisted() const
795  {
796  // Assume the lock is held
797  // If we don't have anything, return MAX
798  if (!_blockStore.back())
799  {
800  return getUnsetSequence();
801  }
802  return _blockStore.back()->_sequence;
803  }
804 
805  amps_uint64_t _getLastPersisted(void)
806  {
807  // Assume the lock is held
808  amps_uint64_t lastPersisted = (amps_uint64_t)0;
809  Buffer* pBuffer = _blockStore.getBuffer();
810  pBuffer->setPosition(_metadataBlock->_offset + 8);
811  lastPersisted = pBuffer->getUint64();
812  if (lastPersisted)
813  {
814  if (_lastSequence < lastPersisted)
815  {
816  _lastSequence = lastPersisted;
817  }
818  return lastPersisted;
819  }
820  if (_maxDiscarded)
821  {
822  lastPersisted = _maxDiscarded;
823  }
824  else
825  {
826 #ifdef _WIN32
827  struct _timeb t;
828  _ftime_s(&t);
829  lastPersisted = (t.time * 1000 + t.millitm) * (amps_uint64_t)1000000;
830 #else // not _WIN32
831  struct timeval tv;
832  gettimeofday(&tv, NULL);
833  lastPersisted = (amps_uint64_t)((tv.tv_sec * 1000) + (tv.tv_usec / 1000))
834  * (amps_uint64_t)1000000;
835 #endif
836  }
837  if (_lastSequence > 2)
838  {
839  amps_uint64_t low = _getLowestUnpersisted();
840  amps_uint64_t high = _getHighestUnpersisted();
841  if (low != getUnsetSequence())
842  {
843  lastPersisted = low - 1;
844  }
845  if (high != getUnsetSequence() && _lastSequence <= high)
846  {
847  _lastSequence = high;
848  }
849  if (_lastSequence < lastPersisted)
850  {
851  lastPersisted = _lastSequence - 1;
852  }
853  }
854  else
855  {
856  _lastSequence = lastPersisted;
857  }
858  pBuffer->setPosition(_metadataBlock->_offset
859  + sizeof(amps_uint32_t) // blocks used
860  + sizeof(amps_uint32_t)); // record length
861  pBuffer->putUint64(lastPersisted);
862  _metadataBlock->_sequence = lastPersisted;
863  return lastPersisted;
864  }
865 
866  void recover(void)
867  {
868  BufferLock bufferGuard(_blockStore);
869  // Make sure the size isn't 0 and is a multiple of block size
870  Buffer* pBuffer = _blockStore.getBuffer();
871  size_t size = pBuffer->getSize();
872  amps_uint32_t blockSize = getBlockSize();
873  if (size == 0)
874  {
875  _blockStore.init();
876  _metadataBlock = _blockStore.get(1);
877  _metadataBlock->_sequence = (amps_uint64_t)0;
878  pBuffer->setPosition(_metadataBlock->_offset);
879  // Metadata block holds block size, block header size,
880  // last discarded sequence, client version
881  pBuffer->putUint32(blockSize);
882  pBuffer->putUint32((amps_uint32_t)getBlockHeaderSize());
883  pBuffer->putUint64((amps_uint64_t)0);
884  // Metadata blocks puts client version in CRC position
885  pBuffer->putUint64((amps_uint64_t)VersionInfo::parseVersion(AMPS_CLIENT_VERSION));
886  // No next in chain
887  pBuffer->putUint64((amps_uint64_t)0);
888  return;
889  }
890  size_t numBlocks = size / blockSize;
891  if (size % blockSize > 0)
892  {
893  // We shouldn't ever be in here, since it requires starting with a
894  // file that is not an even multiple of block size and we always
895  // fix the size.
896  size_t existingBlocks = size / blockSize;
897  numBlocks = existingBlocks;
898  ++numBlocks;
899  if (numBlocks > (std::numeric_limits<size_t>::max)() / blockSize)
900  {
901  throw StoreException("Publish Store size overflow during recovery.");
902  }
903  size_t alignedSize = numBlocks * blockSize;
904  amps_uint32_t blockCount = 0;
905  // We allocate all the Blocks at once below so delete allocated Block[]
906  delete[] _blockStore.resizeBuffer(alignedSize, &blockCount);
907  // Resize can fail if resizeHandler is set and refuses the request
908  // Since this is recovery, we need to simply fail in that case
909  size_t actualSize = pBuffer->getSize();
910  size_t actualBlocks = actualSize / blockSize;
911  if (alignedSize > actualSize || actualSize % blockSize
912  || actualBlocks < numBlocks
913  || actualBlocks - existingBlocks != (size_t)blockCount)
914  {
915  throw StoreException("Publish Store could not resize correctly during recovery, possibly due to resizeHandler refusing the request.");
916  }
917  numBlocks = actualBlocks;
918  size = actualSize;
919  }
920 
921  amps_uint64_t maxIdx = 0;
922  amps_uint64_t minIdx = 0;
923  size_t location = 0;
924  BlockHeader blockHeader;
925  // The blocks we create here all get their offset set in below loop
926  Block* blocks = new Block[numBlocks];
927  blocks[numBlocks - 1]._nextInList = 0;
928  size_t blockNum = 0;
929  _blockStore.addBlocks(blocks);
930  _metadataBlock = blocks; // The first Block is metadata
931  _metadataBlock->_nextInList = 0;
932  pBuffer->setPosition(0);
933  pBuffer->copyBytes((char*)&blockHeader, sizeof(BlockHeader));
934  /* Metadata Block header fields
935  * amps_uint32_t _blocksToWrite = BlockSize
936  * amps_uint32_t _totalRemaining = BlockHeaderSize
937  * amps_uint64_t _seq = last persisted sequence number
938  * amps_uint64_t _crcVal = unused
939  * amps_uint64_t _nextInChain = unused
940  */
941  if (blockHeader._blocksToWrite == 1) // Old store format?
942  {
943  // Unreadable format, fail
944  throw StoreException("Unrecognized format for Store. Can't recover.");
945  }
946  if (blockHeader._blocksToWrite == 0)
947  {
948  pBuffer->setPosition(0);
949  pBuffer->putUint32(blockSize);
950  }
951  else
952  {
953  blockSize = blockHeader._blocksToWrite;
954  _blockStore.setBlockSize(blockSize);
955  }
956  if (blockHeader._totalRemaining == 0)
957  {
958  pBuffer->setPosition(sizeof(amps_uint32_t));
959  pBuffer->putUint32((amps_uint32_t)getBlockHeaderSize());
960  }
961  else
962  {
963  _blockStore.setBlockHeaderSize(blockHeader._totalRemaining);
964  }
965  _metadataBlock->_sequence = blockHeader._seq;
966  if (_metadataBlock->_sequence
967  && _metadataBlock->_sequence < (amps_uint64_t)1000000)
968  {
969  pBuffer->setPosition(_metadataBlock->_offset
970  + sizeof(amps_uint32_t) // BlockSize
971  + sizeof(amps_uint32_t)); // BlockHeaderSize
972  pBuffer->putUint64((amps_uint64_t)0);
973  _metadataBlock->_sequence = 0;
974  }
975  else
976  {
977  // Set _maxDiscarded and _lastSequence
978  _maxDiscarded = _metadataBlock->_sequence;
979  _lastSequence = _maxDiscarded;
980  }
981  // This would be where to check the client version string
982  // No checks currently
983  location += blockSize;
984  amps_uint32_t freeCount = 0;
985  Block* firstFree = NULL;
986  Block* endOfFreeList = NULL;
987  // Used to create used list in order after recovery
988  typedef std::map<amps_uint64_t, Block*> RecoverMap;
989  RecoverMap recoveredBlocks;
990  while (location < size)
991  {
992  // Get index and check if non-zero
993  pBuffer->setPosition(location);
994  pBuffer->copyBytes((char*)&blockHeader, sizeof(BlockHeader));
995  if ((blockHeader._seq > 0 && blockHeader._totalRemaining < size) &&
996  (!blockHeader._crcVal || recoveredBlocks.count(blockHeader._seq)))
997  {
998  // Block is part of a chain
999  location += blockSize;
1000  continue;
1001  }
1002  Block* block = blocks[++blockNum].setOffset(location);
1003  bool recovered = false;
1004  if (blockHeader._seq > 0 && blockHeader._totalRemaining < size)
1005  {
1006  blockHeader._totalRemaining -= (amps_uint32_t)getBlockChainHeaderSize();
1007  block->_sequence = blockHeader._seq;
1008  // Track min and max
1009  if (maxIdx < blockHeader._seq)
1010  {
1011  maxIdx = blockHeader._seq;
1012  }
1013  if (minIdx > blockHeader._seq)
1014  {
1015  minIdx = blockHeader._seq;
1016  }
1017  // Save it in recovered blocks
1018  recoveredBlocks[blockHeader._seq] = block;
1019  // Set up the chain
1020  while (blockHeader._nextInChain != (amps_uint64_t)0)
1021  {
1022  Block* chain = blocks[++blockNum]
1023  .setOffset((size_t)blockHeader._nextInChain);
1024  chain->_nextInList = 0;
1025  pBuffer->setPosition((size_t)blockHeader._nextInChain
1026  + sizeof(amps_uint32_t) // blocks used
1027  + sizeof(amps_uint32_t) // record length
1028  + sizeof(amps_uint64_t) // seq
1029  + sizeof(amps_uint64_t)); // crc
1030  blockHeader._nextInChain = pBuffer->getUint64();
1031  block->_nextInChain = chain;
1032  block = chain;
1033  block->_sequence = blockHeader._seq;
1034  }
1035  recovered = true;
1036  }
1037  if (!recovered)
1038  {
1039  // Put this Block on the free list
1040  if (endOfFreeList)
1041  {
1042  endOfFreeList->_nextInList = block;
1043  }
1044  else
1045  {
1046  firstFree = block;
1047  }
1048  endOfFreeList = block;
1049  ++freeCount;
1050  }
1051  location += blockSize;
1052  }
1053  if (endOfFreeList)
1054  {
1055  endOfFreeList->_nextInList = 0;
1056  }
1057  _blockStore.setFreeList(firstFree, freeCount);
1058  if (maxIdx > _lastSequence)
1059  {
1060  _lastSequence = maxIdx;
1061  }
1062  if (minIdx > _maxDiscarded + 1)
1063  {
1064  _maxDiscarded = minIdx - 1;
1065  }
1066  if (_maxDiscarded > _metadataBlock->_sequence)
1067  {
1068  _metadataBlock->_sequence = _maxDiscarded;
1069  pBuffer->setPosition(_metadataBlock->_offset + 8);
1070  pBuffer->putUint64(_maxDiscarded);
1071  }
1072  Block* end = NULL;
1073  AMPS_FETCH_ADD(&_stored, (long)(recoveredBlocks.size()));
1074  for (RecoverMap::iterator i = recoveredBlocks.begin();
1075  i != recoveredBlocks.end(); ++i)
1076  {
1077  if (end)
1078  {
1079  end->_nextInList = i->second;
1080  }
1081  else
1082  {
1083  _blockStore.setUsedList(i->second);
1084  }
1085  end = i->second;
1086  }
1087  if (end)
1088  {
1089  end->_nextInList = 0;
1090  }
1091  _blockStore.setEndOfUsedList(end);
1092  }
1093 
1094  private:
1095  // Lock should already be held
1096  void replayOnto(Block* block_, StoreReplayer& replayer_)
1097  {
1098  // Read the header
1099  size_t start = block_->_offset;
1100  size_t position = start;
1101  Buffer* pBuffer = _blockStore.getBuffer();
1102  pBuffer->setPosition(position);
1103  BlockHeader blockHeader;
1104  pBuffer->copyBytes((char*)&blockHeader, sizeof(BlockHeader));
1105  if (blockHeader._totalRemaining == 0)
1106  {
1107  // Queue acking sow_delete
1108  return;
1109  }
1110  position += getBlockHeaderSize();
1111  BlockChainHeader blockChainHeader;
1112  pBuffer->copyBytes((char*)&blockChainHeader, sizeof(blockChainHeader));
1113  if (blockChainHeader._operation == Message::Command::Unknown)
1114  {
1115  // Queue acking sow_delete
1116  return;
1117  }
1118  blockChainHeader._ackTypes |= Message::AckType::Persisted;
1119  position += getBlockChainHeaderSize();
1120  blockHeader._totalRemaining -= (amps_uint32_t)getBlockChainHeaderSize();
1121  pBuffer->setPosition(position);
1122 
1123  if (blockHeader._totalRemaining
1124  < blockChainHeader._commandIdLen
1125  + blockChainHeader._correlationIdLen
1126  + blockChainHeader._expirationLen
1127  + blockChainHeader._sowKeyLen
1128  + blockChainHeader._topicLen)
1129  {
1130  std::ostringstream os;
1131  os << "Corrupted message found with invalid lengths. "
1132  << "Attempting to replay " << block_->_sequence
1133  << ". Block sequence " << blockHeader._seq
1134  << ", topic length " << blockChainHeader._topicLen
1135  << ", data length " << blockHeader._totalRemaining
1136  << ", command ID length " << blockChainHeader._commandIdLen
1137  << ", correlation ID length " << blockChainHeader._correlationIdLen
1138  << ", expiration length " << blockChainHeader._expirationLen
1139  << ", sow key length " << blockChainHeader._sowKeyLen
1140  << ", start " << start
1141  << ", position " << position
1142  << ", buffer size " << pBuffer->getSize();
1143  throw StoreException(os.str());
1144  }
1145 
1146  // Start prepping the message
1147  _message.reset();
1148  _message.setCommandEnum((Message::Command::Type)blockChainHeader._operation);
1149  _message.setAckTypeEnum((unsigned)blockChainHeader._ackTypes
1150  | Message::AckType::Persisted);
1151  _message.setSequence(blockHeader._seq);
1152  // Read the data and calculate the CRC
1153  Block* current = block_;
1154  size_t blockBytesRemaining = getBlockDataSize() - getBlockChainHeaderSize();
1155  amps_uint64_t crcCalc = (amps_uint64_t)0ULL;
1156  // Use tmpBuffers for any fields split across Block boundaries
1157  char** tmpBuffers = (blockHeader._blocksToWrite > 1) ? new char* [blockHeader._blocksToWrite - 1] : 0;
1158  size_t blockNum = 0;
1159  if (blockChainHeader._commandIdLen > 0)
1160  {
1161  if (blockChainHeader._commandIdLen <= blockBytesRemaining)
1162  {
1163  _message.assignCommandId(pBuffer->getBytes(blockChainHeader._commandIdLen)._data,
1164  blockChainHeader._commandIdLen);
1165  blockBytesRemaining -= blockChainHeader._commandIdLen;
1166  }
1167  else
1168  {
1169  tmpBuffers[blockNum] = new char[blockChainHeader._commandIdLen]; // -V522
1170  size_t totalLeft = blockChainHeader._commandIdLen;
1171  size_t totalRead = 0;
1172  size_t readLen = 0;
1173  while (totalLeft)
1174  {
1175  readLen = blockBytesRemaining < totalLeft ?
1176  blockBytesRemaining : totalLeft;
1177  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1178  if (!(totalLeft -= readLen))
1179  {
1180  break;
1181  }
1182  if (!(current = current->_nextInChain))
1183  {
1184  break;
1185  }
1186  totalRead += readLen;
1187  blockBytesRemaining = getBlockDataSize();
1188  position = current->_offset + getBlockHeaderSize();
1189  pBuffer->setPosition(position);
1190  }
1191  blockBytesRemaining -= readLen;
1192  _message.assignCommandId(tmpBuffers[blockNum++], blockChainHeader._commandIdLen);
1193  }
1194  blockHeader._totalRemaining -= blockChainHeader._commandIdLen;
1195  crcCalc = _crc(_message.getCommandId().data(),
1196  blockChainHeader._commandIdLen, crcCalc);
1197  }
1198  if (blockChainHeader._correlationIdLen > 0)
1199  {
1200  if (blockChainHeader._correlationIdLen <= blockBytesRemaining)
1201  {
1202  _message.assignCorrelationId(
1203  pBuffer->getBytes(blockChainHeader._correlationIdLen)._data,
1204  blockChainHeader._correlationIdLen);
1205  blockBytesRemaining -= blockChainHeader._correlationIdLen;
1206  }
1207  else
1208  {
1209  tmpBuffers[blockNum] = new char[blockChainHeader._correlationIdLen]; // -V522
1210  size_t totalLeft = blockChainHeader._correlationIdLen;
1211  size_t totalRead = 0;
1212  size_t readLen = 0;
1213  while (totalLeft)
1214  {
1215  readLen = blockBytesRemaining < totalLeft ?
1216  blockBytesRemaining : totalLeft;
1217  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1218  if (!(totalLeft -= readLen))
1219  {
1220  break;
1221  }
1222  if (!(current = current->_nextInChain)) // -V522
1223  {
1224  break;
1225  }
1226  totalRead += readLen;
1227  blockBytesRemaining = getBlockDataSize();
1228  position = current->_offset + getBlockHeaderSize();
1229  pBuffer->setPosition(position);
1230  }
1231  blockBytesRemaining -= readLen;
1232  _message.assignCorrelationId(tmpBuffers[blockNum++], blockChainHeader._correlationIdLen);
1233  }
1234  blockHeader._totalRemaining -= blockChainHeader._correlationIdLen;
1235  crcCalc = _crc(_message.getCorrelationId().data(),
1236  blockChainHeader._correlationIdLen, crcCalc);
1237  }
1238  if (blockChainHeader._expirationLen > 0)
1239  {
1240  if (blockChainHeader._expirationLen <= blockBytesRemaining)
1241  {
1242  _message.assignExpiration(
1243  pBuffer->getBytes(blockChainHeader._expirationLen)._data,
1244  blockChainHeader._expirationLen);
1245  blockBytesRemaining -= blockChainHeader._expirationLen;
1246  }
1247  else
1248  {
1249  tmpBuffers[blockNum] = new char[blockChainHeader._expirationLen]; // -V522
1250  size_t totalLeft = blockChainHeader._expirationLen;
1251  size_t totalRead = 0;
1252  size_t readLen = 0;
1253  while (totalLeft)
1254  {
1255  readLen = blockBytesRemaining < totalLeft ?
1256  blockBytesRemaining : totalLeft;
1257  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1258  if (!(totalLeft -= readLen))
1259  {
1260  break;
1261  }
1262  if (!(current = current->_nextInChain))
1263  {
1264  break;
1265  }
1266  totalRead += readLen;
1267  blockBytesRemaining = getBlockDataSize();
1268  position = current->_offset + getBlockHeaderSize();
1269  pBuffer->setPosition(position);
1270  }
1271  blockBytesRemaining -= readLen;
1272  _message.assignExpiration(tmpBuffers[blockNum++], blockChainHeader._expirationLen);
1273  }
1274  blockHeader._totalRemaining -= blockChainHeader._expirationLen;
1275  crcCalc = _crc(_message.getExpiration().data(),
1276  blockChainHeader._expirationLen, crcCalc);
1277  }
1278  if (blockChainHeader._sowKeyLen > 0)
1279  {
1280  if (blockChainHeader._sowKeyLen <= blockBytesRemaining)
1281  {
1282  _message.assignSowKey(pBuffer->getBytes(blockChainHeader._sowKeyLen)._data,
1283  blockChainHeader._sowKeyLen);
1284  blockBytesRemaining -= blockChainHeader._sowKeyLen;
1285  }
1286  else
1287  {
1288  tmpBuffers[blockNum] = new char[blockChainHeader._sowKeyLen]; // -V522
1289  size_t totalLeft = blockChainHeader._sowKeyLen;
1290  size_t totalRead = 0;
1291  size_t readLen = 0;
1292  while (totalLeft)
1293  {
1294  readLen = blockBytesRemaining < totalLeft ?
1295  blockBytesRemaining : totalLeft;
1296  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1297  if (!(totalLeft -= readLen))
1298  {
1299  break;
1300  }
1301  if (!(current = current->_nextInChain))
1302  {
1303  break;
1304  }
1305  totalRead += readLen;
1306  blockBytesRemaining = getBlockDataSize();
1307  position = current->_offset + getBlockHeaderSize();
1308  pBuffer->setPosition(position);
1309  }
1310  blockBytesRemaining -= readLen;
1311  _message.assignSowKey(tmpBuffers[blockNum++], blockChainHeader._sowKeyLen);
1312  }
1313  blockHeader._totalRemaining -= blockChainHeader._sowKeyLen;
1314  crcCalc = _crc(_message.getSowKey().data(), blockChainHeader._sowKeyLen, crcCalc);
1315  }
1316  if (blockChainHeader._topicLen > 0)
1317  {
1318  if (blockChainHeader._topicLen <= blockBytesRemaining)
1319  {
1320  _message.assignTopic(pBuffer->getBytes(blockChainHeader._topicLen)._data,
1321  blockChainHeader._topicLen);
1322  blockBytesRemaining -= blockChainHeader._topicLen;
1323  }
1324  else
1325  {
1326  tmpBuffers[blockNum] = new char[blockChainHeader._topicLen]; // -V522
1327  size_t totalLeft = blockChainHeader._topicLen;
1328  size_t totalRead = 0;
1329  size_t readLen = 0;
1330  while (totalLeft)
1331  {
1332  readLen = blockBytesRemaining < totalLeft ?
1333  blockBytesRemaining : totalLeft;
1334  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1335  if (!(totalLeft -= readLen))
1336  {
1337  break;
1338  }
1339  if (!(current = current->_nextInChain))
1340  {
1341  break;
1342  }
1343  totalRead += readLen;
1344  blockBytesRemaining = getBlockDataSize();
1345  position = current->_offset + getBlockHeaderSize();
1346  pBuffer->setPosition(position);
1347  }
1348  blockBytesRemaining -= readLen;
1349  _message.assignTopic(tmpBuffers[blockNum++], blockChainHeader._topicLen);
1350  }
1351  blockHeader._totalRemaining -= blockChainHeader._topicLen;
1352  crcCalc = _crc(_message.getTopic().data(), blockChainHeader._topicLen, crcCalc);
1353  }
1354  if (blockHeader._totalRemaining > 0)
1355  {
1356  if (blockHeader._totalRemaining <= blockBytesRemaining)
1357  {
1358  if (blockChainHeader._flag == -1 || blockChainHeader._flag == SOW_DELETE_DATA)
1359  {
1360  _message.assignData(
1361  pBuffer->getBytes(blockHeader._totalRemaining)._data,
1362  blockHeader._totalRemaining);
1363  crcCalc = _crc(_message.getData().data(),
1364  blockHeader._totalRemaining, crcCalc);
1365  }
1366  else if (blockChainHeader._flag == SOW_DELETE_FILTER)
1367  {
1368  _message.assignFilter(
1369  pBuffer->getBytes(blockHeader._totalRemaining)._data,
1370  blockHeader._totalRemaining);
1371  crcCalc = _crc(_message.getFilter().data(),
1372  blockHeader._totalRemaining, crcCalc);
1373  }
1374  else if (blockChainHeader._flag == SOW_DELETE_KEYS)
1375  {
1376  _message.assignSowKeys(
1377  pBuffer->getBytes(blockHeader._totalRemaining)._data,
1378  blockHeader._totalRemaining);
1379  crcCalc = _crc(_message.getSowKeys().data(),
1380  blockHeader._totalRemaining, crcCalc);
1381  }
1382  else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK)
1383  {
1384  _message.assignBookmark(
1385  pBuffer->getBytes(blockHeader._totalRemaining)._data,
1386  blockHeader._totalRemaining);
1387  crcCalc = _crc(_message.getBookmark().data(),
1388  blockHeader._totalRemaining, crcCalc);
1389  }
1390  else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK_CANCEL)
1391  {
1392  _message.assignBookmark(
1393  pBuffer->getBytes(blockHeader._totalRemaining)._data,
1394  blockHeader._totalRemaining);
1395  crcCalc = _crc(_message.getBookmark().data(),
1396  blockHeader._totalRemaining, crcCalc);
1397  _message.assignOptions(AMPS_OPTIONS_CANCEL, 6);
1398  }
1399  }
1400  else
1401  {
1402  tmpBuffers[blockNum] = new char[blockHeader._totalRemaining]; // -V522
1403  size_t totalLeft = blockHeader._totalRemaining;
1404  size_t totalRead = 0;
1405  size_t readLen = 0;
1406  while (totalLeft)
1407  {
1408  readLen = blockBytesRemaining < totalLeft ?
1409  blockBytesRemaining : totalLeft;
1410  pBuffer->copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1411  if (!(totalLeft -= readLen))
1412  {
1413  break;
1414  }
1415  if (!(current = current->_nextInChain))
1416  {
1417  break;
1418  }
1419  totalRead += readLen;
1420  blockBytesRemaining = getBlockDataSize();
1421  position = current->_offset + getBlockHeaderSize();
1422  pBuffer->setPosition(position);
1423  }
1424  position += readLen;
1425  if (blockChainHeader._flag == -1 || blockChainHeader._flag == SOW_DELETE_DATA)
1426  {
1427  _message.assignData(tmpBuffers[blockNum], blockHeader._totalRemaining);
1428  }
1429  else if (blockChainHeader._flag == SOW_DELETE_FILTER)
1430  {
1431  _message.assignFilter(tmpBuffers[blockNum], blockHeader._totalRemaining);
1432  }
1433  else if (blockChainHeader._flag == SOW_DELETE_KEYS)
1434  {
1435  _message.assignSowKeys(tmpBuffers[blockNum], blockHeader._totalRemaining);
1436  }
1437  else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK)
1438  {
1439  _message.assignBookmark(tmpBuffers[blockNum], blockHeader._totalRemaining);
1440  }
1441  else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK_CANCEL)
1442  {
1443  _message.assignBookmark(tmpBuffers[blockNum], blockHeader._totalRemaining);
1444  _message.assignOptions(AMPS_OPTIONS_CANCEL, 6);
1445  }
1446  crcCalc = _crc(tmpBuffers[blockNum++], blockHeader._totalRemaining, crcCalc); // -V595
1447  }
1448  }
1449 
1450  // Validate the crc and seq
1451  if (crcCalc != blockHeader._crcVal || blockHeader._seq != block_->_sequence)
1452  {
1453  std::ostringstream os;
1454  os << "Corrupted message found by CRC or sequence "
1455  << "Attempting to replay " << block_->_sequence
1456  << ". Block sequence " << blockHeader._seq
1457  << ", expiration length " << blockChainHeader._expirationLen
1458  << ", sowKey length " << blockChainHeader._sowKeyLen
1459  << ", topic length " << blockChainHeader._topicLen
1460  << ", data length " << blockHeader._totalRemaining
1461  << ", command ID length " << blockChainHeader._commandIdLen
1462  << ", correlation ID length " << blockChainHeader._correlationIdLen
1463  << ", flag " << blockChainHeader._flag
1464  << ", expected CRC " << blockHeader._crcVal
1465  << ", actual CRC " << crcCalc
1466  << ", start " << start
1467  << ", position " << position
1468  << ", buffer size " << pBuffer->getSize();
1469  for (Block* block = block_; block; block = block->_nextInChain)
1470  {
1471  os << "\n BLOCK " << block->_offset;
1472  }
1473  if (tmpBuffers)
1474  {
1475  for (amps_uint32_t i = 0; i < blockNum; ++i)
1476  {
1477  delete[] tmpBuffers[i]; // -V522
1478  }
1479  delete[] tmpBuffers;
1480  }
1481  throw StoreException(os.str());
1482  }
1483  // Replay the message
1484  replayer_.execute(_message);
1485  // Free the buffer if allocated
1486  if (tmpBuffers)
1487  {
1488  for (amps_uint32_t i = 0; i < blockNum; ++i)
1489  {
1490  delete[] tmpBuffers[i]; // -V522
1491  }
1492  delete[] tmpBuffers;
1493  }
1494  }
1495 
1496  // For recovering an old format store to current format when more Blocks
1497  // are needed with the new format.
1498  void expandBlocks(Block* blocks_, size_t location_, Block* first_,
1499  BlockHeader blockHeader_,
1500  Block** pFreeList_, amps_uint32_t* pFreeCount_,
1501  Buffer* pBuffer_)
1502  {
1503  // First create the chain, then we'll fill in reverse
1504  Block* current = first_;
1505  // Old format total was storage bytes plus 64 bytes for block
1506  // and chain headers.
1507  amps_uint32_t oldTotalRemaining = blockHeader_._totalRemaining;
1508  blockHeader_._totalRemaining -= 64;
1509  // New format counts only chain header size
1510  blockHeader_._totalRemaining += getBlockChainHeaderSize();
1511  // Check how many Blocks needed and if we have enough free.
1512  amps_uint32_t blocksNeeded = blockHeader_._totalRemaining
1513  / getBlockDataSize()
1514  + (blockHeader_._totalRemaining
1515  % getBlockDataSize()
1516  ? 1 : 0);
1517  // Last data block size, remove bytes saved in first block
1518  // then mod by block size.
1519  const amps_uint32_t blockSize = getBlockSize();
1520  // Old total remaining had all header included
1521  size_t endBlockSize = oldTotalRemaining % blockSize;
1522  if (!endBlockSize)
1523  {
1524  endBlockSize = blockSize;
1525  }
1526  size_t endOfData = 0;
1527  // Hang on to CRC until first block is written
1528  amps_uint64_t crcVal = blockHeader_._crcVal;
1529  blockHeader_._crcVal = 0;
1530 
1531  std::stack<Block*> blocksUsed;
1532  for (amps_uint32_t i = 1; i < blocksNeeded; ++i)
1533  {
1534  blocksUsed.push(current);
1535  current->_sequence = blockHeader_._seq;
1536  if (i >= blockHeader_._blocksToWrite)
1537  {
1538  if (i == blockHeader_._blocksToWrite)
1539  {
1540  endOfData = current->_offset + endBlockSize;
1541  }
1542  current->_nextInChain = *pFreeList_;
1543  --(*pFreeCount_);
1544  *pFreeList_ = (*pFreeList_)->_nextInList;
1545  }
1546  else
1547  {
1548  current->_nextInChain = current->_nextInList;
1549  if (current->_nextInChain)
1550  {
1551  if (current->_offset + blockSize < pBuffer_->getSize())
1552  {
1553  current->_nextInChain->setOffset(current->_offset
1554  + blockSize);
1555  }
1556  else
1557  {
1558  current->_nextInChain->setOffset(blockSize);
1559  }
1560  }
1561  else
1562  {
1563  current->_nextInChain = blocks_[1].init(1, blockSize);
1564  }
1565  if (current->_nextInChain == *pFreeList_)
1566  {
1567  *pFreeList_ = (*pFreeList_)->_nextInList;
1568  --(*pFreeCount_);
1569  }
1570  else
1571  {
1572  for (Block* free = *pFreeList_; free;
1573  free = free->_nextInList)
1574  {
1575  if (free->_nextInList == current->_nextInChain)
1576  {
1577  free->_nextInList = free->_nextInList->_nextInList;
1578  --(*pFreeCount_);
1579  break;
1580  }
1581  }
1582  }
1583  }
1584  current->_nextInList = 0;
1585  current = current->_nextInChain;
1586  }
1587  // Make sure we write the correct number of blocks to write
1588  blockHeader_._blocksToWrite = blocksNeeded;
1589  // Finish setting up current
1590  current->_nextInList = 0;
1591  current->_sequence = blockHeader_._seq;
1592  // Now shift data, starting at the last Block
1593  // The total shift is for number of Blocks beyond the first
1594  // times Block header size, since previous format only wrote
1595  // the header in the first Block and had contiguous data,
1596  // with only wrap from end to beginning of buffer possible.
1597 
1598  // First time through, this is bytes in last block. After,
1599  // it will be block data size.
1600  size_t dataBytes = blockHeader_._totalRemaining % getBlockDataSize();
1601  while (current != first_)
1602  {
1603  size_t chunkBytesAvail = endOfData > location_
1604  ? endOfData - location_
1605  : endOfData - 2048;
1606  if (chunkBytesAvail < dataBytes)
1607  {
1608  // Original was wrapped from end to start of buffer
1609  // Need to copy what's left at start to end of Block,
1610  // then start working from the end.
1611  // This can ONLY occur during wrap because the first
1612  // Block doesn't get moved in this loop.
1613  pBuffer_->copyBytes(current->_offset
1614  + getBlockSize()
1615  - chunkBytesAvail,
1616  getBlockSize(),
1617  chunkBytesAvail);
1618  chunkBytesAvail = dataBytes - chunkBytesAvail;
1619  endOfData = pBuffer_->getSize() - chunkBytesAvail;
1620  pBuffer_->copyBytes(current->_offset + getBlockHeaderSize(),
1621  endOfData,
1622  chunkBytesAvail);
1623  }
1624  else
1625  {
1626  endOfData -= dataBytes;
1627  pBuffer_->copyBytes(current->_offset + getBlockHeaderSize(),
1628  endOfData,
1629  dataBytes);
1630  }
1631  // Set next in chain in block header
1632  blockHeader_._nextInChain = (current->_nextInChain
1633  ? current->_nextInChain->_offset
1634  : (amps_uint64_t)0);
1635  // Write the header for this block
1636  pBuffer_->setPosition(current->_offset);
1637  pBuffer_->putBytes((const char*)&blockHeader_, sizeof(BlockHeader));
1638  current = blocksUsed.top();
1639  blocksUsed.pop();
1640  dataBytes = getBlockDataSize();
1641  }
1642  // Move bytes for chain header expansion from 32 bytes
1643  pBuffer_->copyBytes(first_->_offset
1644  + getBlockHeaderSize()
1646  first_->_offset + getBlockHeaderSize() + 32,
1648  // Set the CRC to indicate first block and set nextInChain
1649  blockHeader_._crcVal = crcVal;
1650  blockHeader_._nextInChain = first_->_nextInChain->_offset;
1651  // Need to reset location to after OLD header:
1652  // amps_uint32_t blocks, amps_uint32_t totalRemaining,
1653  // amps_uint64_t seq, amps_uint64_t crc
1654  pBuffer_->setPosition(location_ + (sizeof(amps_uint32_t) * 2)
1655  + (sizeof(amps_uint64_t) * 2) );
1656  // Read old chain header which uses same order, but not
1657  // as many bytes (everything is 32bit):
1658  // operation, commandIdLen, correlationIdLen,
1659  // expirationLen, sowKeyLen, topicLen, flag, ackTypes
1660  BlockChainHeader chainHeader;
1661  pBuffer_->copyBytes((char*)&chainHeader,
1662  sizeof(amps_uint32_t) * 8);
1663  // Rewrite the header and chain header for first Block.
1664  pBuffer_->setPosition(location_);
1665  pBuffer_->putBytes((const char*)&blockHeader_, sizeof(BlockHeader));
1666  pBuffer_->putBytes((const char*)&chainHeader, sizeof(BlockChainHeader));
1667  }
1668 
1669  void chooseCRC(bool isFile)
1670  {
1671  if (!isFile)
1672  {
1673  _crc = noOpCRC;
1674  return;
1675  }
1676 
1677 #ifndef AMPS_SSE_42
1678  _crc = AMPS::CRC<0>::crcNoSSE;
1679 #else
1680  if (AMPS::CRC<0>::isSSE42Enabled())
1681  {
1682  _crc = AMPS::CRC<0>::crc;
1683  }
1684  else
1685  {
1686  _crc = AMPS::CRC<0>::crcNoSSE;
1687  }
1688 #endif
1689  }
1690 
1691  static amps_uint64_t noOpCRC(const char*, size_t, amps_uint64_t)
1692  {
1693  return 0;
1694  }
1695 
1696  protected:
1697  mutable BlockStore _blockStore;
1698  private:
1699  // Block used to hold metadata, currently:
1700  // the last persisted
1701  Block* _metadataBlock;
1702  // Highest sequence that has been discarded
1703  amps_uint64_t _maxDiscarded;
1704  // Track the assigned sequence numbers
1705 #if __cplusplus >= 201103L || _MSC_VER >= 1900
1706  std::atomic<amps_uint64_t> _lastSequence;
1707 #else
1708  volatile amps_uint64_t _lastSequence;
1709 #endif
1710  // Track how many messages are stored
1711  AMPS_ATOMIC_TYPE _stored;
1712 
1713  // Message used for doing replay
1714  Message _message;
1715 
1716  typedef amps_uint64_t (*CRCFunction)(const char*, size_t, amps_uint64_t);
1717 
1718  // Function used to calculate the CRC if one is used
1719  CRCFunction _crc;
1720 
1721  };
1722 
1723 }
1724 
1725 #endif
1726 
virtual void putUint64(amps_uint64_t ui_)=0
Put an amps_uint64_t value into the buffer at the current position and advance past it...
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:1097
void getRawCorrelationId(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the CorrelationId header of self in the un...
Definition: Message.hpp:1344
Command::Type getCommandEnum() const
Decode self&#39;s "command" field and return one of the values from Command.
Definition: Message.hpp:1268
Constants
Default constant values for BlockPublishStore.
Definition: BlockPublishStore.hpp:79
void getRawCommandId(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the CommandId header of self in the underl...
Definition: Message.hpp:1342
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:1069
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: BlockPublishStore.hpp:656
void replay(StoreReplayer &replayer_)
Replay all messages in the Store onto the given StoreReplayer.
Definition: BlockPublishStore.hpp:561
void getRawFilter(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the Filter header of self in the underlyin...
Definition: Message.hpp:1346
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:517
virtual void setPosition(size_t position_)=0
Set the buffer postion to a location.
Field getOptions() const
Retrieves the value of the Options header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1356
static amps_uint32_t getBlockHeaderSize()
Block header is number of blocks, total length, sequence number, crc, next in chain offset...
Definition: BlockPublishStore.hpp:153
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:291
amps_uint32_t getBlockDataSize()
Return the size left in a block for data when it has a header in it.
Definition: BlockPublishStore.hpp:179
virtual size_t getSize() const =0
Get the current size of the Buffer in bytes.
virtual void putBytes(const char *data_, size_t dataLength_)=0
Put the given length of bytes in data into the buffer at the current position and advance past them...
Base class for all exceptions in AMPS.
Definition: AMPSException.hpp:40
virtual void execute(Message &message_)=0
Called by implementations of Store to replay a message from the store.
void getRawBookmark(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the Bookmark header of self in the underly...
Definition: Message.hpp:1234
bool replaySingle(StoreReplayer &replayer_, amps_uint64_t index_)
Replay one message in the Store onto the given StoreReplayer.
Definition: BlockPublishStore.hpp:593
amps_uint64_t store(const Message &message_, bool assignSequence_)
Store a given message that will be delivered to AMPS.
Definition: BlockPublishStore.hpp:266
Used as a base class for other stores in the AMPS C++ client, this is an implementation that breaks a...
Definition: BlockStore.hpp:59
amps_uint64_t getLastPersisted(void)
Get the last persisted sequence number.
Definition: BlockPublishStore.hpp:711
Core type, function, and class declarations for the AMPS C++ client.
size_t unpersistedCount() const
Method to return the count of messages that currently in the Store because they have not been discard...
Definition: BlockPublishStore.hpp:642
Provides AMPS::Buffer, an abstract base class used by the store implementations in the AMPS client...
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:298
virtual void setResizeHandler(PublishStoreResizeHandler handler_, void *userData_)
Set a handler to be called if the Store needs to resize in order to keep storing messages.
Definition: ampsplusplus.hpp:1191
virtual void putUint32(amps_uint32_t i_)=0
Put an unsigned 32-bit int value into the buffer at the current position and advance past the end of ...
void getRawSowKeys(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the SowKeys header of self in the underlyi...
Definition: Message.hpp:1465
Used as a base class for other stores in the AMPS C++ client, this is an implementation of StoreImpl ...
Definition: BlockPublishStore.hpp:60
Abstract base class for implementing a buffer to be used by a StoreImpl for storage of publish messag...
Definition: Buffer.hpp:40
virtual ~BlockPublishStore()
Destructor that cleans up the buffer and other associated memory.
Definition: BlockPublishStore.hpp:241
void getRawExpiration(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the Expiration header of self in the under...
Definition: Message.hpp:1345
static amps_uint32_t getBlockChainHeaderSize()
Block chain header is operation, command id length, correlation id length, expiration length...
Definition: BlockPublishStore.hpp:163
virtual amps_uint64_t getUint64()=0
Get an unsigned 64-bit int value at the current buffer position and advance past it.
void getRawTopic(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the Topic header of self in the underlying...
Definition: Message.hpp:1489
Used as metadata for each block in a Buffer.
Definition: BlockStore.hpp:74
Field represents the value of a single field in a Message.
Definition: Field.hpp:87
virtual amps_uint32_t getUint32()=0
Get the unsigned 32-bit int value at the current buffer position and advance past it...
virtual amps_uint64_t store(const Message &message_)
Store a given message that will be delivered to AMPS.
Definition: BlockPublishStore.hpp:251
BlockPublishStore(Buffer *buffer_, amps_uint32_t blocksPerRealloc_=1000, bool isFile_=false, bool errorOnPublishGap_=false, amps_uint32_t blockSize_=DEFAULT_BLOCK_SIZE)
Create a BlockPublishStore using buffer_, that grows by blocksPerRealloc_ blocks when it must grow...
Definition: BlockPublishStore.hpp:200
amps_uint32_t getBlockSize()
Return the size left in a block for data when it has a header in it.
Definition: BlockPublishStore.hpp:171
void getRawSowKey(const char **dataptr, size_t *sizeptr) const
Modifies the passed in arguments to reference the value of the SowKey header of self in the underlyin...
Definition: Message.hpp:1464
unsigned getAckTypeEnum() const
Decode self&#39;s "ack type" field and return the corresponding bitmask of values from AckType...
Definition: Message.hpp:1200
amps_uint64_t getLowestUnpersisted() const
Get the oldest unpersisted message sequence in the store.
Definition: BlockPublishStore.hpp:699
Definition: AMPSException.hpp:32
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:1167
virtual void copyBytes(char *buffer_, size_t numBytes_)=0
Copy the given number of bytes from this buffer to the given buffer.
virtual void discardUpTo(amps_uint64_t index_)
Remove all messages with an index up to and including index_.
Definition: BlockPublishStore.hpp:498
Provides AMPS::BlockStore, a class for storing Blocks of a fixed size into a Buffer implementation...
virtual ByteArray getBytes(size_t numBytes_)=0
Get the given number of bytes from the buffer.