26 #ifndef _BLOCKPUBLISHSTORE_H_ 27 #define _BLOCKPUBLISHSTORE_H_ 35 #include <amps/ampscrc.hpp> 36 #if __cplusplus >= 201103L || _MSC_VER >= 1900 42 #include <sys/timeb.h> 64 typedef Lock<BlockStore> BufferLock;
65 typedef Unlock<BlockStore> BufferUnlock;
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
81 DEFAULT_BLOCK_HEADER_SIZE = 32,
82 DEFAULT_BLOCK_CHAIN_HEADER_SIZE = 64,
83 DEFAULT_BLOCKS_PER_REALLOC = 1000,
84 DEFAULT_BLOCK_SIZE = 2048
124 amps_uint32_t _blocksToWrite;
125 amps_uint32_t _totalRemaining;
127 amps_uint64_t _crcVal;
128 amps_uint64_t _nextInChain;
131 struct BlockChainHeader
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;
140 amps_uint32_t _ackTypes;
141 amps_uint32_t _unused[8];
143 : _operation(0), _commandIdLen(0), _correlationIdLen(0)
144 , _expirationLen(0), _sowKeyLen(0), _topicLen(0), _flag(-1)
155 return DEFAULT_BLOCK_HEADER_SIZE;
165 return DEFAULT_BLOCK_CHAIN_HEADER_SIZE;
173 return _blockStore.getBlockSize();
201 amps_uint32_t blocksPerRealloc_ = 1000,
202 bool isFile_ =
false,
203 bool errorOnPublishGap_ =
false,
204 amps_uint32_t blockSize_ = DEFAULT_BLOCK_SIZE)
206 , _blockStore(buffer_, normalizeBlocksPerRealloc(blocksPerRealloc_, blockSize_),
207 DEFAULT_BLOCK_HEADER_SIZE,
208 normalizeBlockSize(blocksPerRealloc_, blockSize_))
210 , _maxDiscarded((amps_uint64_t)0), _lastSequence((amps_uint64_t)1)
218 BufferLock bufferGuard(_blockStore);
220 _metadataBlock = _blockStore.get(1);
222 _blockStore.setUsedList(0);
223 _blockStore.setEndOfUsedList(0);
226 _metadataBlock->_sequence = (amps_uint64_t)0;
227 Buffer* pBuffer = _blockStore.getBuffer();
233 pBuffer->
putUint64((amps_uint64_t)VersionInfo::parseVersion(AMPS_CLIENT_VERSION));
253 return store(message_,
true);
268 const char* commandId, *correlationId, *expiration, *sowKey,
271 BlockHeader blockHeader;
272 BlockChainHeader chainHeader;
274 chainHeader._commandIdLen = (amps_uint32_t)dataLen;
276 chainHeader._correlationIdLen = (amps_uint32_t)dataLen;
278 chainHeader._expirationLen = (amps_uint32_t)dataLen;
280 chainHeader._sowKeyLen = (amps_uint32_t)dataLen;
282 chainHeader._topicLen = (amps_uint32_t)dataLen;
283 message_.getRawData(&data, &dataLen);
284 chainHeader._flag = -1;
286 chainHeader._operation = (amps_uint32_t)operation;
287 if (operation == Message::Command::SOWDelete)
291 chainHeader._flag = SOW_DELETE_DATA;
298 chainHeader._flag = SOW_DELETE_FILTER;
305 chainHeader._flag = SOW_DELETE_KEYS;
310 chainHeader._flag = SOW_DELETE_BOOKMARK;
313 size_t remaining = options.
len();
314 const void* next = NULL;
315 const void* start = (
const void*)(options.
data());
317 while (remaining >= 6 &&
318 (next = memchr(start, (
int)
'c', remaining)) != NULL)
320 remaining = (size_t)next - (
size_t)start;
321 if (remaining >= 6 && strncmp((
const char*)start,
324 chainHeader._flag = SOW_DELETE_BOOKMARK_CANCEL;
332 blockHeader._totalRemaining = (
333 (chainHeader._operation == Message::Command::Unknown)
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 :
344 blockHeader._blocksToWrite = ((operation == Message::Command::Unknown)
346 : ((amps_uint32_t)(blockHeader._totalRemaining
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);
368 BufferLock bufferGuard(_blockStore);
369 Block* first = _blockStore.get(blockHeader._blocksToWrite);
372 if (_lastSequence <= 2)
376 blockHeader._seq = ++_lastSequence;
381 message_.getMessage(),
385 _maxDiscarded = blockHeader._seq - 1;
387 if (blockHeader._seq >= _lastSequence)
389 _lastSequence = blockHeader._seq;
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)
404 next->_sequence = blockHeader._seq;
405 if (next->_nextInChain)
407 blockHeader._nextInChain = next->_nextInChain->_offset;
411 blockHeader._nextInChain = (amps_uint64_t)0;
415 pBuffer->
putBytes((
const char*)&blockHeader,
sizeof(BlockHeader));
417 blockHeader._crcVal = (amps_uint64_t)0;
423 pBuffer->
putBytes((
const char*)&chainHeader,
424 sizeof(BlockChainHeader));
433 if (commandWritten < chainHeader._commandIdLen)
435 size_t commandWrite = (chainHeader._commandIdLen - commandWritten < bytesRemaining) ? chainHeader._commandIdLen - commandWritten : bytesRemaining;
436 pBuffer->
putBytes(commandId + commandWritten,
438 bytesRemaining -= commandWrite;
439 commandWritten += commandWrite;
441 if (correlationWritten < chainHeader._correlationIdLen)
443 size_t correlationWrite = (chainHeader._correlationIdLen - correlationWritten < bytesRemaining) ? chainHeader._correlationIdLen - correlationWritten : bytesRemaining;
444 pBuffer->
putBytes(correlationId + correlationWritten,
446 bytesRemaining -= correlationWrite;
447 correlationWritten += correlationWrite;
449 if (bytesRemaining > 0 && expirationWritten < chainHeader._expirationLen)
451 size_t expWrite = (chainHeader._expirationLen - expirationWritten < bytesRemaining) ? chainHeader._expirationLen - expirationWritten : bytesRemaining;
452 pBuffer->
putBytes(expiration + expirationWritten, expWrite);
453 bytesRemaining -= expWrite;
454 expirationWritten += expWrite;
456 if (bytesRemaining > 0 && sowKeyWritten < chainHeader._sowKeyLen)
458 size_t sowKeyWrite = (chainHeader._sowKeyLen - sowKeyWritten < bytesRemaining) ? chainHeader._sowKeyLen - sowKeyWritten : bytesRemaining;
459 pBuffer->
putBytes(sowKey + sowKeyWritten, sowKeyWrite);
460 bytesRemaining -= sowKeyWrite;
461 sowKeyWritten += sowKeyWrite;
463 if (bytesRemaining > 0 && topicWritten < chainHeader._topicLen)
465 size_t topicWrite = (chainHeader._topicLen - topicWritten
467 ? chainHeader._topicLen - topicWritten
469 pBuffer->
putBytes(topic + topicWritten, topicWrite);
470 bytesRemaining -= topicWrite;
471 topicWritten += topicWrite;
473 if (bytesRemaining > 0 && dataWritten < dataLen)
475 size_t dataWrite = (dataLen - dataWritten < bytesRemaining) ?
476 dataLen - dataWritten : bytesRemaining;
477 pBuffer->
putBytes(data + dataWritten, dataWrite);
478 bytesRemaining -= dataWrite;
479 dataWritten += dataWrite;
485 _blockStore.put(first);
488 AMPS_FETCH_ADD(&_stored, 1);
489 return blockHeader._seq;
501 BufferLock bufferGuard(_blockStore);
502 Buffer* pBuffer = _blockStore.getBuffer();
505 amps_uint64_t lastPersisted = _metadataBlock->_sequence;
507 if (index_ == (amps_uint64_t)0 || !_blockStore.front() || index_ <= _maxDiscarded)
511 if (lastPersisted < index_)
515 _metadataBlock->_sequence = index_;
516 if (_maxDiscarded < index_)
518 _maxDiscarded = index_;
520 if (_lastSequence <= index_)
522 _lastSequence = index_;
529 else if (getErrorOnPublishGap() && index_ < lastPersisted)
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());
537 _blockStore.signalAll();
541 _maxDiscarded = index_;
542 AMPS_FETCH_SUB(&_stored, _blockStore.put(index_));
543 _blockStore.signalAll();
544 if (lastPersisted >= index_)
550 _metadataBlock->_sequence = index_;
551 if (_lastSequence < index_)
553 _lastSequence = index_;
564 BufferLock bufferGuard(_blockStore);
566 if (!_blockStore.front())
570 Block* next = _blockStore.front();
573 for (Block* block = _blockStore.front(); block; block = next)
576 replayOnto(block, replayer_);
577 next = block->_nextInList;
580 catch (
const StoreException&)
582 _blockStore.putAll(next);
595 BufferLock bufferGuard(_blockStore);
597 if (!_blockStore.front())
602 amps_uint64_t lastIdx = _blockStore.back()->_sequence;
604 amps_uint64_t leastIdx = _blockStore.front()->_sequence;
605 if (index_ >= leastIdx && index_ <= lastIdx)
607 Block* block = _blockStore.front();
608 while (block && block->_sequence != index_)
610 block = block->_nextInList;
617 Buffer* pBuffer = _blockStore.getBuffer();
619 sizeof(amps_uint32_t));
624 replayOnto(block, replayer_);
631 _message.setSequence(leastIdx);
644 size_t count = (size_t)_stored;
658 BufferLock bufferGuard(_blockStore);
659 amps_uint64_t waitFor = _getHighestUnpersisted();
667 bool timedOut =
false;
668 AMPS_START_TIMER(timeout_);
670 while (!timedOut && _stored != 0
671 && waitFor >= _getLowestUnpersisted())
673 if (!_blockStore.wait(timeout_))
676 AMPS_RESET_TIMER(timedOut, timeout_);
680 if (timedOut && _stored != 0
681 && waitFor >= _getLowestUnpersisted())
683 throw TimedOutException(
"Timed out waiting to flush publish store.");
688 while (_stored != 0 && waitFor >= _getLowestUnpersisted())
691 _blockStore.wait(1000);
693 BufferUnlock unlck(_blockStore);
694 amps_invoke_waiting_function();
701 BufferLock bufferGuard(_blockStore);
702 return _getLowestUnpersisted();
705 amps_uint64_t getHighestUnpersisted()
const 707 BufferLock bufferGuard(_blockStore);
708 return _getHighestUnpersisted();
713 BufferLock bufferGuard(_blockStore);
714 return _getLastPersisted();
718 static size_t normalizeBlockCountValue(
size_t blocksPerRealloc_)
720 if (blocksPerRealloc_ == 0)
722 return DEFAULT_BLOCKS_PER_REALLOC;
724 if (blocksPerRealloc_ > BlockStore::MAX_BLOCKS_PER_REALLOC)
726 return BlockStore::MAX_BLOCKS_PER_REALLOC;
728 return blocksPerRealloc_;
731 static amps_uint32_t normalizeBlockSizeValue(amps_uint32_t blockSize_)
735 return DEFAULT_BLOCK_SIZE;
737 if (blockSize_ < DEFAULT_BLOCK_CHAIN_HEADER_SIZE * 2)
739 return DEFAULT_BLOCK_CHAIN_HEADER_SIZE * 2;
741 return blockSize_ & ~127U;
744 static amps_uint32_t normalizeBlocksPerRealloc(
size_t blocksPerRealloc_,
745 amps_uint32_t blockSize_)
747 size_t blocks = normalizeBlockCountValue(blocksPerRealloc_);
748 amps_uint32_t blockSize = normalizeBlockSizeValue(blockSize_);
750 if (blocks > (std::numeric_limits<size_t>::max)() / blockSize)
752 return DEFAULT_BLOCKS_PER_REALLOC;
754 return (amps_uint32_t)blocks;
757 static amps_uint32_t normalizeBlockSize(
size_t blocksPerRealloc_,
758 amps_uint32_t blockSize_)
760 size_t blocks = normalizeBlockCountValue(blocksPerRealloc_);
761 amps_uint32_t blockSize = normalizeBlockSizeValue(blockSize_);
763 if (blocks > (std::numeric_limits<size_t>::max)() / blockSize)
765 return DEFAULT_BLOCK_SIZE;
770 static size_t getInitialBufferSize(
size_t blocksPerRealloc_,
771 amps_uint32_t blockSize_)
773 return (
size_t)normalizeBlocksPerRealloc(blocksPerRealloc_, blockSize_)
774 * normalizeBlockSize(blocksPerRealloc_, blockSize_);
777 static bool canResize(
size_t requestedSize_,
void* vpThis_)
780 return me->callResizeHandler(requestedSize_);
783 amps_uint64_t _getLowestUnpersisted()
const 787 if (!_blockStore.front())
791 return _blockStore.front()->_sequence;
794 amps_uint64_t _getHighestUnpersisted()
const 798 if (!_blockStore.back())
802 return _blockStore.back()->_sequence;
805 amps_uint64_t _getLastPersisted(
void)
808 amps_uint64_t lastPersisted = (amps_uint64_t)0;
809 Buffer* pBuffer = _blockStore.getBuffer();
814 if (_lastSequence < lastPersisted)
816 _lastSequence = lastPersisted;
818 return lastPersisted;
822 lastPersisted = _maxDiscarded;
829 lastPersisted = (t.time * 1000 + t.millitm) * (amps_uint64_t)1000000;
832 gettimeofday(&tv, NULL);
833 lastPersisted = (amps_uint64_t)((tv.tv_sec * 1000) + (tv.tv_usec / 1000))
834 * (amps_uint64_t)1000000;
837 if (_lastSequence > 2)
839 amps_uint64_t low = _getLowestUnpersisted();
840 amps_uint64_t high = _getHighestUnpersisted();
843 lastPersisted = low - 1;
847 _lastSequence = high;
849 if (_lastSequence < lastPersisted)
851 lastPersisted = _lastSequence - 1;
856 _lastSequence = lastPersisted;
859 +
sizeof(amps_uint32_t)
860 +
sizeof(amps_uint32_t));
862 _metadataBlock->_sequence = lastPersisted;
863 return lastPersisted;
868 BufferLock bufferGuard(_blockStore);
870 Buffer* pBuffer = _blockStore.getBuffer();
871 size_t size = pBuffer->
getSize();
876 _metadataBlock = _blockStore.get(1);
877 _metadataBlock->_sequence = (amps_uint64_t)0;
885 pBuffer->
putUint64((amps_uint64_t)VersionInfo::parseVersion(AMPS_CLIENT_VERSION));
890 size_t numBlocks = size / blockSize;
891 if (size % blockSize > 0)
896 size_t existingBlocks = size / blockSize;
897 numBlocks = existingBlocks;
899 if (numBlocks > (std::numeric_limits<size_t>::max)() / blockSize)
901 throw StoreException(
"Publish Store size overflow during recovery.");
903 size_t alignedSize = numBlocks * blockSize;
904 amps_uint32_t blockCount = 0;
906 delete[] _blockStore.resizeBuffer(alignedSize, &blockCount);
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)
915 throw StoreException(
"Publish Store could not resize correctly during recovery, possibly due to resizeHandler refusing the request.");
917 numBlocks = actualBlocks;
921 amps_uint64_t maxIdx = 0;
922 amps_uint64_t minIdx = 0;
924 BlockHeader blockHeader;
926 Block* blocks =
new Block[numBlocks];
927 blocks[numBlocks - 1]._nextInList = 0;
929 _blockStore.addBlocks(blocks);
930 _metadataBlock = blocks;
931 _metadataBlock->_nextInList = 0;
933 pBuffer->
copyBytes((
char*)&blockHeader,
sizeof(BlockHeader));
941 if (blockHeader._blocksToWrite == 1)
944 throw StoreException(
"Unrecognized format for Store. Can't recover.");
946 if (blockHeader._blocksToWrite == 0)
953 blockSize = blockHeader._blocksToWrite;
954 _blockStore.setBlockSize(blockSize);
956 if (blockHeader._totalRemaining == 0)
963 _blockStore.setBlockHeaderSize(blockHeader._totalRemaining);
965 _metadataBlock->_sequence = blockHeader._seq;
966 if (_metadataBlock->_sequence
967 && _metadataBlock->_sequence < (amps_uint64_t)1000000)
970 +
sizeof(amps_uint32_t)
971 +
sizeof(amps_uint32_t));
973 _metadataBlock->_sequence = 0;
978 _maxDiscarded = _metadataBlock->_sequence;
979 _lastSequence = _maxDiscarded;
983 location += blockSize;
984 amps_uint32_t freeCount = 0;
985 Block* firstFree = NULL;
986 Block* endOfFreeList = NULL;
988 typedef std::map<amps_uint64_t, Block*> RecoverMap;
989 RecoverMap recoveredBlocks;
990 while (location < size)
994 pBuffer->
copyBytes((
char*)&blockHeader,
sizeof(BlockHeader));
995 if ((blockHeader._seq > 0 && blockHeader._totalRemaining < size) &&
996 (!blockHeader._crcVal || recoveredBlocks.count(blockHeader._seq)))
999 location += blockSize;
1002 Block* block = blocks[++blockNum].setOffset(location);
1003 bool recovered =
false;
1004 if (blockHeader._seq > 0 && blockHeader._totalRemaining < size)
1007 block->_sequence = blockHeader._seq;
1009 if (maxIdx < blockHeader._seq)
1011 maxIdx = blockHeader._seq;
1013 if (minIdx > blockHeader._seq)
1015 minIdx = blockHeader._seq;
1018 recoveredBlocks[blockHeader._seq] = block;
1020 while (blockHeader._nextInChain != (amps_uint64_t)0)
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)
1027 +
sizeof(amps_uint32_t)
1028 +
sizeof(amps_uint64_t)
1029 +
sizeof(amps_uint64_t));
1030 blockHeader._nextInChain = pBuffer->
getUint64();
1031 block->_nextInChain = chain;
1033 block->_sequence = blockHeader._seq;
1042 endOfFreeList->_nextInList = block;
1048 endOfFreeList = block;
1051 location += blockSize;
1055 endOfFreeList->_nextInList = 0;
1057 _blockStore.setFreeList(firstFree, freeCount);
1058 if (maxIdx > _lastSequence)
1060 _lastSequence = maxIdx;
1062 if (minIdx > _maxDiscarded + 1)
1064 _maxDiscarded = minIdx - 1;
1066 if (_maxDiscarded > _metadataBlock->_sequence)
1068 _metadataBlock->_sequence = _maxDiscarded;
1069 pBuffer->
setPosition(_metadataBlock->_offset + 8);
1073 AMPS_FETCH_ADD(&_stored, (
long)(recoveredBlocks.size()));
1074 for (RecoverMap::iterator i = recoveredBlocks.begin();
1075 i != recoveredBlocks.end(); ++i)
1079 end->_nextInList = i->second;
1083 _blockStore.setUsedList(i->second);
1089 end->_nextInList = 0;
1091 _blockStore.setEndOfUsedList(end);
1099 size_t start = block_->_offset;
1100 size_t position = start;
1101 Buffer* pBuffer = _blockStore.getBuffer();
1103 BlockHeader blockHeader;
1104 pBuffer->
copyBytes((
char*)&blockHeader,
sizeof(BlockHeader));
1105 if (blockHeader._totalRemaining == 0)
1111 BlockChainHeader blockChainHeader;
1112 pBuffer->
copyBytes((
char*)&blockChainHeader,
sizeof(blockChainHeader));
1113 if (blockChainHeader._operation == Message::Command::Unknown)
1118 blockChainHeader._ackTypes |= Message::AckType::Persisted;
1123 if (blockHeader._totalRemaining
1124 < blockChainHeader._commandIdLen
1125 + blockChainHeader._correlationIdLen
1126 + blockChainHeader._expirationLen
1127 + blockChainHeader._sowKeyLen
1128 + blockChainHeader._topicLen)
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());
1148 _message.setCommandEnum((Message::Command::Type)blockChainHeader._operation);
1149 _message.setAckTypeEnum((
unsigned)blockChainHeader._ackTypes
1150 | Message::AckType::Persisted);
1151 _message.setSequence(blockHeader._seq);
1153 Block* current = block_;
1155 amps_uint64_t crcCalc = (amps_uint64_t)0ULL;
1157 char** tmpBuffers = (blockHeader._blocksToWrite > 1) ?
new char* [blockHeader._blocksToWrite - 1] : 0;
1158 size_t blockNum = 0;
1159 if (blockChainHeader._commandIdLen > 0)
1161 if (blockChainHeader._commandIdLen <= blockBytesRemaining)
1163 _message.assignCommandId(pBuffer->
getBytes(blockChainHeader._commandIdLen)._data,
1164 blockChainHeader._commandIdLen);
1165 blockBytesRemaining -= blockChainHeader._commandIdLen;
1169 tmpBuffers[blockNum] =
new char[blockChainHeader._commandIdLen];
1170 size_t totalLeft = blockChainHeader._commandIdLen;
1171 size_t totalRead = 0;
1175 readLen = blockBytesRemaining < totalLeft ?
1176 blockBytesRemaining : totalLeft;
1177 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1178 if (!(totalLeft -= readLen))
1182 if (!(current = current->_nextInChain))
1186 totalRead += readLen;
1191 blockBytesRemaining -= readLen;
1192 _message.assignCommandId(tmpBuffers[blockNum++], blockChainHeader._commandIdLen);
1194 blockHeader._totalRemaining -= blockChainHeader._commandIdLen;
1195 crcCalc = _crc(_message.getCommandId().data(),
1196 blockChainHeader._commandIdLen, crcCalc);
1198 if (blockChainHeader._correlationIdLen > 0)
1200 if (blockChainHeader._correlationIdLen <= blockBytesRemaining)
1202 _message.assignCorrelationId(
1203 pBuffer->
getBytes(blockChainHeader._correlationIdLen)._data,
1204 blockChainHeader._correlationIdLen);
1205 blockBytesRemaining -= blockChainHeader._correlationIdLen;
1209 tmpBuffers[blockNum] =
new char[blockChainHeader._correlationIdLen];
1210 size_t totalLeft = blockChainHeader._correlationIdLen;
1211 size_t totalRead = 0;
1215 readLen = blockBytesRemaining < totalLeft ?
1216 blockBytesRemaining : totalLeft;
1217 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1218 if (!(totalLeft -= readLen))
1222 if (!(current = current->_nextInChain))
1226 totalRead += readLen;
1231 blockBytesRemaining -= readLen;
1232 _message.assignCorrelationId(tmpBuffers[blockNum++], blockChainHeader._correlationIdLen);
1234 blockHeader._totalRemaining -= blockChainHeader._correlationIdLen;
1235 crcCalc = _crc(_message.getCorrelationId().data(),
1236 blockChainHeader._correlationIdLen, crcCalc);
1238 if (blockChainHeader._expirationLen > 0)
1240 if (blockChainHeader._expirationLen <= blockBytesRemaining)
1242 _message.assignExpiration(
1243 pBuffer->
getBytes(blockChainHeader._expirationLen)._data,
1244 blockChainHeader._expirationLen);
1245 blockBytesRemaining -= blockChainHeader._expirationLen;
1249 tmpBuffers[blockNum] =
new char[blockChainHeader._expirationLen];
1250 size_t totalLeft = blockChainHeader._expirationLen;
1251 size_t totalRead = 0;
1255 readLen = blockBytesRemaining < totalLeft ?
1256 blockBytesRemaining : totalLeft;
1257 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1258 if (!(totalLeft -= readLen))
1262 if (!(current = current->_nextInChain))
1266 totalRead += readLen;
1271 blockBytesRemaining -= readLen;
1272 _message.assignExpiration(tmpBuffers[blockNum++], blockChainHeader._expirationLen);
1274 blockHeader._totalRemaining -= blockChainHeader._expirationLen;
1275 crcCalc = _crc(_message.getExpiration().data(),
1276 blockChainHeader._expirationLen, crcCalc);
1278 if (blockChainHeader._sowKeyLen > 0)
1280 if (blockChainHeader._sowKeyLen <= blockBytesRemaining)
1282 _message.assignSowKey(pBuffer->
getBytes(blockChainHeader._sowKeyLen)._data,
1283 blockChainHeader._sowKeyLen);
1284 blockBytesRemaining -= blockChainHeader._sowKeyLen;
1288 tmpBuffers[blockNum] =
new char[blockChainHeader._sowKeyLen];
1289 size_t totalLeft = blockChainHeader._sowKeyLen;
1290 size_t totalRead = 0;
1294 readLen = blockBytesRemaining < totalLeft ?
1295 blockBytesRemaining : totalLeft;
1296 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1297 if (!(totalLeft -= readLen))
1301 if (!(current = current->_nextInChain))
1305 totalRead += readLen;
1310 blockBytesRemaining -= readLen;
1311 _message.assignSowKey(tmpBuffers[blockNum++], blockChainHeader._sowKeyLen);
1313 blockHeader._totalRemaining -= blockChainHeader._sowKeyLen;
1314 crcCalc = _crc(_message.getSowKey().data(), blockChainHeader._sowKeyLen, crcCalc);
1316 if (blockChainHeader._topicLen > 0)
1318 if (blockChainHeader._topicLen <= blockBytesRemaining)
1320 _message.assignTopic(pBuffer->
getBytes(blockChainHeader._topicLen)._data,
1321 blockChainHeader._topicLen);
1322 blockBytesRemaining -= blockChainHeader._topicLen;
1326 tmpBuffers[blockNum] =
new char[blockChainHeader._topicLen];
1327 size_t totalLeft = blockChainHeader._topicLen;
1328 size_t totalRead = 0;
1332 readLen = blockBytesRemaining < totalLeft ?
1333 blockBytesRemaining : totalLeft;
1334 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1335 if (!(totalLeft -= readLen))
1339 if (!(current = current->_nextInChain))
1343 totalRead += readLen;
1348 blockBytesRemaining -= readLen;
1349 _message.assignTopic(tmpBuffers[blockNum++], blockChainHeader._topicLen);
1351 blockHeader._totalRemaining -= blockChainHeader._topicLen;
1352 crcCalc = _crc(_message.getTopic().data(), blockChainHeader._topicLen, crcCalc);
1354 if (blockHeader._totalRemaining > 0)
1356 if (blockHeader._totalRemaining <= blockBytesRemaining)
1358 if (blockChainHeader._flag == -1 || blockChainHeader._flag == SOW_DELETE_DATA)
1360 _message.assignData(
1361 pBuffer->
getBytes(blockHeader._totalRemaining)._data,
1362 blockHeader._totalRemaining);
1363 crcCalc = _crc(_message.getData().data(),
1364 blockHeader._totalRemaining, crcCalc);
1366 else if (blockChainHeader._flag == SOW_DELETE_FILTER)
1368 _message.assignFilter(
1369 pBuffer->
getBytes(blockHeader._totalRemaining)._data,
1370 blockHeader._totalRemaining);
1371 crcCalc = _crc(_message.getFilter().data(),
1372 blockHeader._totalRemaining, crcCalc);
1374 else if (blockChainHeader._flag == SOW_DELETE_KEYS)
1376 _message.assignSowKeys(
1377 pBuffer->
getBytes(blockHeader._totalRemaining)._data,
1378 blockHeader._totalRemaining);
1379 crcCalc = _crc(_message.getSowKeys().data(),
1380 blockHeader._totalRemaining, crcCalc);
1382 else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK)
1384 _message.assignBookmark(
1385 pBuffer->
getBytes(blockHeader._totalRemaining)._data,
1386 blockHeader._totalRemaining);
1387 crcCalc = _crc(_message.getBookmark().data(),
1388 blockHeader._totalRemaining, crcCalc);
1390 else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK_CANCEL)
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);
1402 tmpBuffers[blockNum] =
new char[blockHeader._totalRemaining];
1403 size_t totalLeft = blockHeader._totalRemaining;
1404 size_t totalRead = 0;
1408 readLen = blockBytesRemaining < totalLeft ?
1409 blockBytesRemaining : totalLeft;
1410 pBuffer->
copyBytes(tmpBuffers[blockNum] + totalRead, readLen);
1411 if (!(totalLeft -= readLen))
1415 if (!(current = current->_nextInChain))
1419 totalRead += readLen;
1424 position += readLen;
1425 if (blockChainHeader._flag == -1 || blockChainHeader._flag == SOW_DELETE_DATA)
1427 _message.assignData(tmpBuffers[blockNum], blockHeader._totalRemaining);
1429 else if (blockChainHeader._flag == SOW_DELETE_FILTER)
1431 _message.assignFilter(tmpBuffers[blockNum], blockHeader._totalRemaining);
1433 else if (blockChainHeader._flag == SOW_DELETE_KEYS)
1435 _message.assignSowKeys(tmpBuffers[blockNum], blockHeader._totalRemaining);
1437 else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK)
1439 _message.assignBookmark(tmpBuffers[blockNum], blockHeader._totalRemaining);
1441 else if (blockChainHeader._flag == SOW_DELETE_BOOKMARK_CANCEL)
1443 _message.assignBookmark(tmpBuffers[blockNum], blockHeader._totalRemaining);
1444 _message.assignOptions(AMPS_OPTIONS_CANCEL, 6);
1446 crcCalc = _crc(tmpBuffers[blockNum++], blockHeader._totalRemaining, crcCalc);
1451 if (crcCalc != blockHeader._crcVal || blockHeader._seq != block_->_sequence)
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)
1471 os <<
"\n BLOCK " << block->_offset;
1475 for (amps_uint32_t i = 0; i < blockNum; ++i)
1477 delete[] tmpBuffers[i];
1479 delete[] tmpBuffers;
1481 throw StoreException(os.str());
1488 for (amps_uint32_t i = 0; i < blockNum; ++i)
1490 delete[] tmpBuffers[i];
1492 delete[] tmpBuffers;
1498 void expandBlocks(Block* blocks_,
size_t location_, Block* first_,
1499 BlockHeader blockHeader_,
1500 Block** pFreeList_, amps_uint32_t* pFreeCount_,
1504 Block* current = first_;
1507 amps_uint32_t oldTotalRemaining = blockHeader_._totalRemaining;
1508 blockHeader_._totalRemaining -= 64;
1512 amps_uint32_t blocksNeeded = blockHeader_._totalRemaining
1514 + (blockHeader_._totalRemaining
1521 size_t endBlockSize = oldTotalRemaining % blockSize;
1524 endBlockSize = blockSize;
1526 size_t endOfData = 0;
1528 amps_uint64_t crcVal = blockHeader_._crcVal;
1529 blockHeader_._crcVal = 0;
1531 std::stack<Block*> blocksUsed;
1532 for (amps_uint32_t i = 1; i < blocksNeeded; ++i)
1534 blocksUsed.push(current);
1535 current->_sequence = blockHeader_._seq;
1536 if (i >= blockHeader_._blocksToWrite)
1538 if (i == blockHeader_._blocksToWrite)
1540 endOfData = current->_offset + endBlockSize;
1542 current->_nextInChain = *pFreeList_;
1544 *pFreeList_ = (*pFreeList_)->_nextInList;
1548 current->_nextInChain = current->_nextInList;
1549 if (current->_nextInChain)
1551 if (current->_offset + blockSize < pBuffer_->getSize())
1553 current->_nextInChain->setOffset(current->_offset
1558 current->_nextInChain->setOffset(blockSize);
1563 current->_nextInChain = blocks_[1].init(1, blockSize);
1565 if (current->_nextInChain == *pFreeList_)
1567 *pFreeList_ = (*pFreeList_)->_nextInList;
1572 for (Block* free = *pFreeList_; free;
1573 free = free->_nextInList)
1575 if (free->_nextInList == current->_nextInChain)
1577 free->_nextInList = free->_nextInList->_nextInList;
1584 current->_nextInList = 0;
1585 current = current->_nextInChain;
1588 blockHeader_._blocksToWrite = blocksNeeded;
1590 current->_nextInList = 0;
1591 current->_sequence = blockHeader_._seq;
1601 while (current != first_)
1603 size_t chunkBytesAvail = endOfData > location_
1604 ? endOfData - location_
1606 if (chunkBytesAvail < dataBytes)
1618 chunkBytesAvail = dataBytes - chunkBytesAvail;
1619 endOfData = pBuffer_->
getSize() - chunkBytesAvail;
1626 endOfData -= dataBytes;
1632 blockHeader_._nextInChain = (current->_nextInChain
1633 ? current->_nextInChain->_offset
1634 : (amps_uint64_t)0);
1637 pBuffer_->
putBytes((
const char*)&blockHeader_,
sizeof(BlockHeader));
1638 current = blocksUsed.top();
1649 blockHeader_._crcVal = crcVal;
1650 blockHeader_._nextInChain = first_->_nextInChain->_offset;
1654 pBuffer_->
setPosition(location_ + (
sizeof(amps_uint32_t) * 2)
1655 + (
sizeof(amps_uint64_t) * 2) );
1660 BlockChainHeader chainHeader;
1661 pBuffer_->
copyBytes((
char*)&chainHeader,
1662 sizeof(amps_uint32_t) * 8);
1665 pBuffer_->
putBytes((
const char*)&blockHeader_,
sizeof(BlockHeader));
1666 pBuffer_->
putBytes((
const char*)&chainHeader,
sizeof(BlockChainHeader));
1669 void chooseCRC(
bool isFile)
1678 _crc = AMPS::CRC<0>::crcNoSSE;
1680 if (AMPS::CRC<0>::isSSE42Enabled())
1682 _crc = AMPS::CRC<0>::crc;
1686 _crc = AMPS::CRC<0>::crcNoSSE;
1691 static amps_uint64_t noOpCRC(
const char*,
size_t, amps_uint64_t)
1701 Block* _metadataBlock;
1703 amps_uint64_t _maxDiscarded;
1705 #if __cplusplus >= 201103L || _MSC_VER >= 1900 1706 std::atomic<amps_uint64_t> _lastSequence;
1708 volatile amps_uint64_t _lastSequence;
1711 AMPS_ATOMIC_TYPE _stored;
1716 typedef amps_uint64_t (*CRCFunction)(
const char*, size_t, amps_uint64_t);
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'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'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.