1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2/* 3 * Copyright 2017 Couchbase, Inc 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include "collections/vbucket_manifest.h" 19#include "checkpoint.h" 20#include "collections/manifest.h" 21#include "collections/vbucket_serialised_manifest_entry.h" 22#include "item.h" 23#include "vbucket.h" 24 25#include <JSON_checker.h> 26#include <cJSON.h> 27#include <cJSON_utils.h> 28#include <platform/make_unique.h> 29 30namespace Collections { 31namespace VB { 32 33Manifest::Manifest(const std::string& manifest) 34 : defaultCollectionExists(false), 35 separator(DefaultSeparator), 36 greatestEndSeqno(StoredValue::state_collection_open), 37 nDeletingCollections(0) { 38 if (manifest.empty()) { 39 // Empty manifest, initialise the manifest with the default collection 40 addNewCollectionEntry({DefaultCollectionIdentifier, 0}, 41 0, 42 StoredValue::state_collection_open); 43 defaultCollectionExists = true; 44 return; 45 } 46 47 if (!checkUTF8JSON(reinterpret_cast<const unsigned char*>(manifest.data()), 48 manifest.size())) { 49 throwException<std::invalid_argument>(__FUNCTION__, 50 "input not valid json"); 51 } 52 53 unique_cJSON_ptr cjson(cJSON_Parse(manifest.c_str())); 54 if (!cjson) { 55 throwException<std::invalid_argument>(__FUNCTION__, 56 "cJSON cannot parse json"); 57 } 58 59 // Load the uid 60 manifestUid = makeUid(getJsonEntry(cjson.get(), "uid")); 61 62 // Load the separator 63 separator = getJsonEntry(cjson.get(), "separator"); 64 65 // Load the collections array 66 auto jsonCollections = cJSON_GetObjectItem(cjson.get(), "collections"); 67 if (!jsonCollections || jsonCollections->type != cJSON_Array) { 68 throwException<std::invalid_argument>( 69 __FUNCTION__, 70 "cannot find valid " 71 "collections: " + 72 (!jsonCollections 73 ? "nullptr" 74 : std::to_string(jsonCollections->type))); 75 } 76 77 // Iterate the collections and load-em up. 78 for (int ii = 0; ii < cJSON_GetArraySize(jsonCollections); ii++) { 79 auto collection = cJSON_GetArrayItem(jsonCollections, ii); 80 uid_t uid = makeUid(getJsonEntry(collection, "uid")); 81 int64_t startSeqno = std::stoll(getJsonEntry(collection, "startSeqno")); 82 int64_t endSeqno = std::stoll(getJsonEntry(collection, "endSeqno")); 83 std::string collectionName(getJsonEntry(collection, "name")); 84 auto& entry = addNewCollectionEntry( 85 {collectionName, uid}, startSeqno, endSeqno); 86 87 if (DefaultCollectionIdentifier == collectionName.c_str()) { 88 defaultCollectionExists = entry.isOpen(); 89 } 90 } 91} 92 93void Manifest::update(::VBucket& vb, const Collections::Manifest& manifest) { 94 std::vector<Collections::Manifest::Identifier> additions, deletions; 95 std::tie(additions, deletions) = processManifest(manifest); 96 97 if (separator != manifest.getSeparator()) { 98 changeSeparator(vb, 99 manifest.getUid(), 100 manifest.getSeparator(), 101 OptionalSeqno{/*no-seqno*/}); 102 } 103 104 // Process deletions to the manifest 105 for (const auto& collection : deletions) { 106 beginCollectionDelete(vb, 107 manifest.getUid(), 108 {collection.getName(), collection.getUid()}, 109 OptionalSeqno{/*no-seqno*/}); 110 } 111 112 // Process additions to the manifest 113 for (const auto& collection : additions) { 114 addCollection(vb, 115 manifest.getUid(), 116 {collection.getName(), collection.getUid()}, 117 OptionalSeqno{/*no-seqno*/}); 118 } 119} 120 121void Manifest::addCollection(::VBucket& vb, 122 uid_t manifestUid, 123 Identifier identifier, 124 OptionalSeqno optionalSeqno) { 125 // 1. Update the manifest, adding or updating an entry in the map. Specify a 126 // non-zero start 127 auto& entry = addCollectionEntry(identifier); 128 129 // 2. Queue a system event, this will take a copy of the manifest ready 130 // for persistence into the vb state file. 131 auto seqno = queueSystemEvent(vb, 132 SystemEvent::Collection, 133 identifier, 134 false /*deleted*/, 135 optionalSeqno); 136 137 // record the uid of the manifest which is adding the collection 138 this->manifestUid = manifestUid; 139 140 LOG(EXTENSION_LOG_NOTICE, 141 "collections: vb:%" PRIu16 " adding collection:%.*s, uid:%" PRIx64 142 ", replica:%s, backfill:%s, seqno:%" PRId64 ", manifest:%" PRIx64, 143 vb.getId(), 144 int(identifier.getName().size()), 145 identifier.getName().data(), 146 identifier.getUid(), 147 optionalSeqno.is_initialized() ? "true" : "false", 148 vb.isBackfillPhase() ? "true" : "false", 149 seqno, 150 manifestUid); 151 152 // 3. Now patch the entry with the seqno of the system event, note the copy 153 // of the manifest taken at step 1 gets the correct seqno when the system 154 // event is flushed. 155 entry.setStartSeqno(seqno); 156} 157 158ManifestEntry& Manifest::addCollectionEntry(Identifier identifier) { 159 auto itr = map.find(identifier.getName()); 160 if (itr == map.end()) { 161 if (identifier.isDefaultCollection()) { 162 defaultCollectionExists = true; 163 } 164 // Add new collection with 0,-6 start,end. The caller will correct the 165 // seqno based on what the checkpoint manager returns. 166 return addNewCollectionEntry( 167 identifier, 0, StoredValue::state_collection_open); 168 } else if (!itr->second->isOpen()) { 169 if (identifier.isDefaultCollection()) { 170 defaultCollectionExists = true; 171 } 172 173 itr->second->setUid(identifier.getUid()); 174 return *itr->second; 175 } 176 throwException<std::logic_error>( 177 __FUNCTION__, "cannot add collection:" + to_string(identifier)); 178} 179 180ManifestEntry& Manifest::addNewCollectionEntry(Identifier identifier, 181 int64_t startSeqno, 182 int64_t endSeqno) { 183 // This method is only for when the map does not have the collection 184 if (map.count(identifier.getName()) > 0) { 185 throwException<std::logic_error>( 186 __FUNCTION__, 187 "collection already exists, collection:" + 188 to_string(identifier) + ", startSeqno:" + 189 std::to_string(startSeqno) + ", endSeqno:" + 190 std::to_string(endSeqno)); 191 } 192 auto m = std::make_unique<ManifestEntry>(identifier, startSeqno, endSeqno); 193 auto* newEntry = m.get(); 194 map.emplace(m->getCharBuffer(), std::move(m)); 195 196 if (newEntry->isDeleting()) { 197 trackEndSeqno(endSeqno); 198 } 199 200 return *newEntry; 201} 202 203void Manifest::beginCollectionDelete(::VBucket& vb, 204 uid_t manifestUid, 205 Identifier identifier, 206 OptionalSeqno optionalSeqno) { 207 auto& entry = beginDeleteCollectionEntry(identifier); 208 auto seqno = queueSystemEvent(vb, 209 SystemEvent::Collection, 210 identifier, 211 true /*deleted*/, 212 optionalSeqno); 213 214 // record the uid of the manifest which removed the collection 215 this->manifestUid = manifestUid; 216 217 LOG(EXTENSION_LOG_NOTICE, 218 "collections: vb:%" PRIu16 219 " begin delete of collection:%.*s, uid:%" PRIx64 220 ", replica:%s, backfill:%s, seqno:%" PRId64 ", manifest:%" PRIx64, 221 vb.getId(), 222 int(identifier.getName().size()), 223 identifier.getName().data(), 224 identifier.getUid(), 225 optionalSeqno.is_initialized() ? "true" : "false", 226 vb.isBackfillPhase() ? "true" : "false", 227 seqno, 228 manifestUid); 229 230 if (identifier.isDefaultCollection()) { 231 defaultCollectionExists = false; 232 } 233 234 entry.setEndSeqno(seqno); 235 236 trackEndSeqno(seqno); 237} 238 239ManifestEntry& Manifest::beginDeleteCollectionEntry(Identifier identifier) { 240 auto itr = map.find(identifier.getName()); 241 if (itr == map.end()) { 242 throwException<std::logic_error>( 243 __FUNCTION__, 244 "did not find collection:" + to_string(identifier)); 245 } 246 247 return *itr->second; 248} 249 250void Manifest::completeDeletion(::VBucket& vb, 251 cb::const_char_buffer collection) { 252 auto itr = map.find(collection); 253 254 LOG(EXTENSION_LOG_NOTICE, 255 "collections: vb:%" PRIu16 " complete delete of collection:%.*s", 256 vb.getId(), 257 int(collection.size()), 258 collection.data()); 259 260 if (itr == map.end()) { 261 throwException<std::logic_error>( 262 __FUNCTION__, 263 "could not find collection:" + cb::to_string(collection)); 264 } 265 266 auto se = itr->second->completeDeletion(); 267 auto uid = itr->second->getUid(); 268 269 if (se == SystemEvent::DeleteCollectionHard) { 270 map.erase(itr); // wipe out 271 } 272 273 nDeletingCollections--; 274 if (nDeletingCollections == 0) { 275 greatestEndSeqno = StoredValue::state_collection_open; 276 } 277 278 queueSystemEvent(vb, 279 se, 280 {collection, uid}, 281 false /*delete*/, 282 OptionalSeqno{/*none*/}); 283} 284 285void Manifest::changeSeparator(::VBucket& vb, 286 uid_t manifestUid, 287 cb::const_char_buffer newSeparator, 288 OptionalSeqno optionalSeqno) { 289 // Can we change the separator? Only allowed to change if there are no 290 // collections or the only collection is the default collection 291 if (cannotChangeSeparator()) { 292 throwException<std::logic_error>(__FUNCTION__, 293 "cannot change " 294 "separator to " + 295 cb::to_string(newSeparator)); 296 } else { 297 LOG(EXTENSION_LOG_NOTICE, 298 "collections: vb:%" PRIu16 299 " changing collection separator from:%s, to:%.*s, replica:%s, " 300 "backfill:%s, manifest:%" PRIx64, 301 vb.getId(), 302 separator.c_str(), 303 int(newSeparator.size()), 304 newSeparator.data(), 305 optionalSeqno.is_initialized() ? "true" : "false", 306 vb.isBackfillPhase() ? "true" : "false", 307 manifestUid); 308 309 // record the uid of the manifest which changed the separator 310 this->manifestUid = manifestUid; 311 312 std::string oldSeparator = separator; 313 // Change the separator then queue the event so the new separator 314 // is recorded in the serialised manifest 315 separator = std::string(newSeparator.data(), newSeparator.size()); 316 317 // Queue an event so that the manifest is flushed and DCP can 318 // replicate the change. 319 (void)queueSeparatorChanged(vb, oldSeparator, optionalSeqno); 320 } 321} 322 323Manifest::processResult Manifest::processManifest( 324 const Collections::Manifest& manifest) const { 325 std::vector<Collections::Manifest::Identifier> additions, deletions; 326 327 for (const auto& entry : map) { 328 // If the entry is open and not found in the new manifest it must be 329 // deleted. 330 if (entry.second->isOpen() && 331 manifest.find(entry.second->getIdentifier()) == manifest.end()) { 332 deletions.push_back(entry.second->getIdentifier()); 333 } 334 } 335 336 // iterate manifest and add all non-existent collection 337 for (const auto& m : manifest) { 338 // if we don't find the collection, then it must be an addition. 339 // if we do find a name match, then check if the collection is in the 340 // process of being deleted or has a new UID 341 auto itr = map.find(m.getName()); 342 343 if (itr == map.end() || !itr->second->isOpen() || 344 itr->second->getUid() != m.getUid()) { 345 additions.push_back(m); 346 } 347 } 348 return std::make_pair(additions, deletions); 349} 350 351bool Manifest::doesKeyContainValidCollection(const ::DocKey& key) const { 352 if (defaultCollectionExists && 353 key.getDocNamespace() == DocNamespace::DefaultCollection) { 354 return true; 355 } else if (key.getDocNamespace() == DocNamespace::Collections) { 356 const auto cKey = Collections::DocKey::make(key, separator); 357 if (cKey.getCollectionLen()) { 358 auto itr = map.find({reinterpret_cast<const char*>(cKey.data()), 359 cKey.getCollectionLen()}); 360 if (itr != map.end()) { 361 return itr->second->isOpen(); 362 } 363 } 364 } 365 return false; 366} 367 368Manifest::container::const_iterator Manifest::getManifestEntry( 369 const ::DocKey& key) const { 370 return getManifestEntry(key, separator); 371} 372 373Manifest::container::const_iterator Manifest::getManifestEntry( 374 const ::DocKey& key, const std::string& separator) const { 375 cb::const_char_buffer identifier; 376 if (defaultCollectionExists && 377 key.getDocNamespace() == DocNamespace::DefaultCollection) { 378 identifier = DefaultCollectionIdentifier; 379 } else if (key.getDocNamespace() == DocNamespace::Collections) { 380 const auto cKey = Collections::DocKey::make(key, separator); 381 identifier = cKey.getCollection(); 382 } else if (key.getDocNamespace() == DocNamespace::System) { 383 const auto cKey = Collections::DocKey::make(key); 384 385 if (cKey.getCollection() == SystemEventPrefix) { 386 identifier = cKey.getKey(); 387 } else { 388 std::string sysKey(reinterpret_cast<const char*>(key.data()), 389 key.size()); 390 throwException<std::invalid_argument>( 391 __FUNCTION__, "Use of system key invalid, key:" + sysKey); 392 } 393 } 394 395 return map.find(identifier); 396} 397 398bool Manifest::isLogicallyDeleted(const ::DocKey& key, int64_t seqno) const { 399 return isLogicallyDeleted(key, seqno, separator); 400} 401 402bool Manifest::isLogicallyDeleted(const ::DocKey& key, 403 int64_t seqno, 404 const std::string& separator) const { 405 // Only do the searching/scanning work for keys in the deleted range. 406 if (seqno <= greatestEndSeqno) { 407 switch (key.getDocNamespace()) { 408 case DocNamespace::DefaultCollection: 409 return !defaultCollectionExists; 410 case DocNamespace::Collections: { 411 const auto cKey = Collections::DocKey::make(key, separator); 412 auto itr = map.find(cKey.getCollection()); 413 if (itr != map.end()) { 414 return seqno <= itr->second->getEndSeqno(); 415 } 416 break; 417 } 418 case DocNamespace::System: { 419 const auto cKey = Collections::DocKey::make(key); 420 if (cKey.getCollection() == SystemEventPrefix) { 421 auto itr = map.find(cKey.getKey()); 422 if (itr != map.end()) { 423 return seqno <= itr->second->getEndSeqno(); 424 } 425 } 426 } 427 } 428 } 429 return false; 430} 431 432bool Manifest::isLogicallyDeleted(const container::const_iterator entry, 433 int64_t seqno) const { 434 if (entry == map.end()) { 435 throwException<std::invalid_argument>( 436 __FUNCTION__, 437 "iterator is invalid, seqno:" + std::to_string(seqno)); 438 } 439 440 if (seqno <= greatestEndSeqno) { 441 return seqno <= entry->second->getEndSeqno(); 442 } 443 return false; 444} 445 446boost::optional<cb::const_char_buffer> Manifest::shouldCompleteDeletion( 447 const ::DocKey& key) const { 448 // If this is a SystemEvent key then... 449 if (key.getDocNamespace() == DocNamespace::System) { 450 const auto cKey = Collections::DocKey::make(key); 451 // 1. Check it's a collection's event 452 if (cKey.getCollection() == SystemEventPrefix) { 453 // 2. Lookup the collection entry 454 auto itr = map.find(cKey.getKey()); 455 if (itr == map.end()) { 456 throwException<std::logic_error>( 457 __FUNCTION__, 458 "SystemEvent found which didn't match a collection " + 459 cb::to_string(cKey.getKey())); 460 } 461 462 // 3. If this collection is deleting, return the collection name. 463 if (itr->second->isDeleting()) { 464 return {cKey.getKey()}; 465 } 466 } 467 } 468 return {}; 469} 470 471std::unique_ptr<Item> Manifest::createSystemEvent(SystemEvent se, 472 Identifier identifier, 473 bool deleted, 474 OptionalSeqno seqno) const { 475 // Create an item (to be queued and written to disk) that represents 476 // the update of a collection and allows the checkpoint to update 477 // the _local document with a persisted version of this object (the entire 478 // manifest is persisted to disk as JSON). 479 // The key for the item includes the name and revision to ensure a 480 // checkpoint consumer (e.g. DCP) can transmit the full collection info. 481 482 auto item = SystemEventFactory::make( 483 se, 484 cb::to_string(identifier.getName()), 485 getSerialisedDataSize(identifier.getName()), 486 seqno); 487 488 // Quite rightly an Item's value is const, but in this case the Item is 489 // owned only by the local scope so is safe to mutate (by const_cast force) 490 populateWithSerialisedData( 491 {const_cast<char*>(item->getData()), item->getNBytes()}, 492 identifier); 493 494 if (deleted) { 495 item->setDeleted(); 496 } 497 498 return item; 499} 500 501std::unique_ptr<Item> Manifest::createSeparatorChangedEvent( 502 int64_t highSeqno, 503 OptionalSeqno seqno) const { 504 // Create an item (to be queued and written to disk) that represents 505 // the change of the separator. The item always has the same key. 506 // We serialise the state of this object into the Item's value. 507 auto item = SystemEventFactory::make( 508 SystemEvent::CollectionsSeparatorChanged, 509 std::to_string(highSeqno) + SystemSeparator + separator, 510 getSerialisedDataSize(), 511 seqno); 512 513 // Quite rightly an Item's value is const, but in this case the Item is 514 // owned only by the local scope so is safe to mutate (by const_cast force) 515 populateWithSerialisedData( 516 {const_cast<char*>(item->getData()), item->getNBytes()}); 517 518 return item; 519} 520 521int64_t Manifest::queueSystemEvent(::VBucket& vb, 522 SystemEvent se, 523 Identifier identifier, 524 bool deleted, 525 OptionalSeqno seq) const { 526 // Create and transfer Item ownership to the VBucket 527 auto rv = vb.queueItem( 528 createSystemEvent(se, identifier, deleted, seq).release(), seq); 529 530 // If seq is not set, then this is an active vbucket queueing the event. 531 // Collection events will end the CP so they don't de-dep. 532 if (!seq.is_initialized()) { 533 vb.checkpointManager->createNewCheckpoint(); 534 } 535 return rv; 536} 537 538int64_t Manifest::queueSeparatorChanged(::VBucket& vb, 539 const std::string& oldSeparator, 540 OptionalSeqno seqno) const { 541 // Create and transfer Item ownership to the VBucket 542 return vb.queueItem( 543 createSeparatorChangedEvent(vb.getHighSeqno(), seqno).release(), 544 seqno); 545} 546 547size_t Manifest::getSerialisedDataSize(cb::const_char_buffer collection) const { 548 size_t bytesNeeded = SerialisedManifest::getObjectSize(separator.size()); 549 for (const auto& collectionEntry : map) { 550 // Skip if a collection in the map matches the collection being changed 551 if (collectionEntry.second->getCharBuffer() == collection) { 552 continue; 553 } 554 bytesNeeded += SerialisedManifestEntry::getObjectSize( 555 collectionEntry.second->getCollectionName().size()); 556 } 557 558 return bytesNeeded + 559 SerialisedManifestEntry::getObjectSize(collection.size()); 560} 561 562size_t Manifest::getSerialisedDataSize() const { 563 size_t bytesNeeded = SerialisedManifest::getObjectSize(separator.size()); 564 for (const auto& collectionEntry : map) { 565 // Skip if a collection in the map matches the collection being changed 566 bytesNeeded += SerialisedManifestEntry::getObjectSize( 567 collectionEntry.second->getCollectionName().size()); 568 } 569 570 return bytesNeeded; 571} 572 573void Manifest::populateWithSerialisedData(cb::char_buffer out, 574 Identifier identifier) const { 575 auto* sMan = SerialisedManifest::make( 576 out.data(), separator, getManifestUid(), out); 577 uint32_t itemCounter = 1; // always a final entry 578 char* serial = sMan->getManifestEntryBuffer(); 579 580 const std::unique_ptr<ManifestEntry>* finalEntry = nullptr; 581 for (const auto& collectionEntry : map) { 582 // Check if we find the mutated entry in the map (so we know if we're 583 // mutating it) 584 if (collectionEntry.second->getCharBuffer() == identifier.getName()) { 585 // If a collection in the map matches the collection being changed 586 // save the iterator so we can use it when creating the final entry 587 finalEntry = &collectionEntry.second; 588 } else { 589 itemCounter++; 590 auto* sme = SerialisedManifestEntry::make( 591 serial, *collectionEntry.second, out); 592 serial = sme->nextEntry(); 593 } 594 } 595 596 SerialisedManifestEntry* finalSme = nullptr; 597 if (finalEntry) { 598 // delete 599 finalSme = 600 SerialisedManifestEntry::make(serial, *finalEntry->get(), out); 601 } else { 602 // create 603 finalSme = SerialisedManifestEntry::make(serial, identifier, out); 604 } 605 606 sMan->setEntryCount(itemCounter); 607 sMan->calculateFinalEntryOffest(finalSme); 608} 609 610void Manifest::populateWithSerialisedData(cb::char_buffer out) const { 611 auto* sMan = SerialisedManifest::make( 612 out.data(), separator, getManifestUid(), out); 613 char* serial = sMan->getManifestEntryBuffer(); 614 615 for (const auto& collectionEntry : map) { 616 auto* sme = SerialisedManifestEntry::make( 617 serial, *collectionEntry.second, out); 618 serial = sme->nextEntry(); 619 } 620 621 sMan->setEntryCount(map.size()); 622} 623 624std::string Manifest::serialToJson(const Item& collectionsEventItem) { 625 cb::const_char_buffer buffer(collectionsEventItem.getData(), 626 collectionsEventItem.getNBytes()); 627 const auto se = SystemEvent(collectionsEventItem.getFlags()); 628 629 if (se == SystemEvent::CollectionsSeparatorChanged) { 630 return serialToJson(buffer); 631 } 632 633 const auto* sMan = 634 reinterpret_cast<const SerialisedManifest*>(buffer.data()); 635 const char* serial = sMan->getManifestEntryBuffer(); 636 637 std::stringstream json; 638 json << R"({"separator":")" << sMan->getSeparator() << R"(","uid":")" 639 << std::hex << sMan->getManifestUid() << R"(","collections":[)"; 640 641 if (sMan->getEntryCount() > 1) { 642 // Iterate and produce an comma separated list 643 for (uint32_t ii = 1; ii < sMan->getEntryCount(); ii++) { 644 const auto* sme = 645 reinterpret_cast<const SerialisedManifestEntry*>(serial); 646 json << sme->toJson(); 647 serial = sme->nextEntry(); 648 649 if (ii < sMan->getEntryCount() - 1) { 650 json << ","; 651 } 652 } 653 654 // DeleteCollectionHard removes this last entry so no comma 655 if (se != SystemEvent::DeleteCollectionHard) { 656 json << ","; 657 } 658 } 659 660 const auto* sme = reinterpret_cast<const SerialisedManifestEntry*>(serial); 661 // Last entry is the collection which changed. How did it change? 662 if (se == SystemEvent::Collection) { 663 // Collection start/end (create/delete) 664 json << sme->toJsonCreateOrDelete(collectionsEventItem.isDeleted(), 665 collectionsEventItem.getBySeqno()); 666 } else if (se == SystemEvent::DeleteCollectionSoft) { 667 // Collection delete completed, but collection has been recreated 668 json << sme->toJsonResetEnd(); 669 } 670 671 json << "]}"; 672 return json.str(); 673} 674 675std::string Manifest::serialToJson(cb::const_char_buffer buffer) { 676 const auto* sMan = 677 reinterpret_cast<const SerialisedManifest*>(buffer.data()); 678 const char* serial = sMan->getManifestEntryBuffer(); 679 680 std::stringstream json; 681 json << R"({"separator":")" << sMan->getSeparator() << R"(","uid":")" 682 << std::hex << sMan->getManifestUid() << R"(","collections":[)"; 683 684 for (uint32_t ii = 0; ii < sMan->getEntryCount(); ii++) { 685 const auto* sme = 686 reinterpret_cast<const SerialisedManifestEntry*>(serial); 687 json << sme->toJson(); 688 serial = sme->nextEntry(); 689 690 if (ii < sMan->getEntryCount() - 1) { 691 json << ","; 692 } 693 } 694 695 json << "]}"; 696 return json.str(); 697} 698 699const char* Manifest::getJsonEntry(cJSON* cJson, const char* key) { 700 auto jsonEntry = cJSON_GetObjectItem(cJson, key); 701 if (!jsonEntry || jsonEntry->type != cJSON_String) { 702 throwException<std::invalid_argument>( 703 __FUNCTION__, 704 "null or not string, key:" + std::string(key) + " " + 705 (!jsonEntry ? "nullptr" 706 : std::to_string(jsonEntry->type))); 707 } 708 return jsonEntry->valuestring; 709} 710 711void Manifest::trackEndSeqno(int64_t seqno) { 712 nDeletingCollections++; 713 if (seqno > greatestEndSeqno || 714 greatestEndSeqno == StoredValue::state_collection_open) { 715 greatestEndSeqno = seqno; 716 } 717} 718 719bool Manifest::cannotChangeSeparator() const { 720 // If any non-default collection exists that isOpen, cannot change separator 721 for (const auto& manifestEntry : map) { 722 // If the manifestEntry is open and not found in the new manifest it 723 // must be deleted. 724 if (manifestEntry.second->isOpen() && 725 manifestEntry.second->getCharBuffer() != 726 DefaultCollectionIdentifier) { 727 // Collection is open and is not $default - cannot change 728 return true; 729 } 730 } 731 732 return false; 733} 734 735SystemEventData Manifest::getSystemEventData( 736 cb::const_char_buffer serialisedManifest) { 737 const auto* sm = reinterpret_cast<const SerialisedManifest*>( 738 serialisedManifest.data()); 739 const auto* sme = sm->getFinalManifestEntry(); 740 return {sm->getManifestUid(), {sme->getCollectionName(), sme->getUid()}}; 741} 742 743SystemEventSeparatorData Manifest::getSystemEventSeparatorData( 744 cb::const_char_buffer serialisedManifest) { 745 const auto* sm = reinterpret_cast<const SerialisedManifest*>( 746 serialisedManifest.data()); 747 return {sm->getManifestUid(), sm->getSeparatorBuffer()}; 748} 749 750std::string Manifest::getExceptionString(const std::string& thrower, 751 const std::string& error) const { 752 std::stringstream ss; 753 ss << "VB::Manifest:" << thrower << ": " << error << ", this:" << *this; 754 return ss.str(); 755} 756 757std::ostream& operator<<(std::ostream& os, const Manifest& manifest) { 758 os << "VB::Manifest" 759 << ": defaultCollectionExists:" << manifest.defaultCollectionExists 760 << ", separator:" << manifest.separator 761 << ", greatestEndSeqno:" << manifest.greatestEndSeqno 762 << ", nDeletingCollections:" << manifest.nDeletingCollections 763 << ", map.size:" << manifest.map.size() << std::endl; 764 for (auto& m : manifest.map) { 765 os << *m.second << std::endl; 766 } 767 768 return os; 769} 770 771std::ostream& operator<<(std::ostream& os, 772 const Manifest::ReadHandle& readHandle) { 773 os << "VB::Manifest::ReadHandle: manifest:" << readHandle.manifest; 774 return os; 775} 776 777std::ostream& operator<<(std::ostream& os, 778 const Manifest::CachingReadHandle& readHandle) { 779 os << "VB::Manifest::CachingReadHandle: itr:"; 780 if (readHandle.iteratorValid()) { 781 os << *readHandle.itr->second; 782 } else { 783 os << "end"; 784 } 785 os << ", manifest:" << readHandle.manifest; 786 return os; 787} 788 789} // end namespace VB 790} // end namespace Collections 791