1126ed5a4SJim Walker/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2126ed5a4SJim Walker/*
3126ed5a4SJim Walker *     Copyright 2017 Couchbase, Inc
4126ed5a4SJim Walker *
5126ed5a4SJim Walker *   Licensed under the Apache License, Version 2.0 (the "License");
6126ed5a4SJim Walker *   you may not use this file except in compliance with the License.
7126ed5a4SJim Walker *   You may obtain a copy of the License at
8126ed5a4SJim Walker *
9126ed5a4SJim Walker *       http://www.apache.org/licenses/LICENSE-2.0
10126ed5a4SJim Walker *
11126ed5a4SJim Walker *   Unless required by applicable law or agreed to in writing, software
12126ed5a4SJim Walker *   distributed under the License is distributed on an "AS IS" BASIS,
13126ed5a4SJim Walker *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14126ed5a4SJim Walker *   See the License for the specific language governing permissions and
15126ed5a4SJim Walker *   limitations under the License.
16126ed5a4SJim Walker */
17126ed5a4SJim Walker
18126ed5a4SJim Walker#include "collections/manager.h"
196e741613SJim Walker#include "collections/filter.h"
20126ed5a4SJim Walker#include "collections/manifest.h"
216e741613SJim Walker#include "ep_engine.h"
22126ed5a4SJim Walker#include "kv_bucket.h"
23126ed5a4SJim Walker#include "vbucket.h"
24126ed5a4SJim Walker
259628a0d6SJim WalkerCollections::Manager::Manager() {
26126ed5a4SJim Walker}
27126ed5a4SJim Walker
28126ed5a4SJim Walkercb::engine_error Collections::Manager::update(KVBucket& bucket,
29126ed5a4SJim Walker                                              const std::string& json) {
30126ed5a4SJim Walker    std::unique_lock<std::mutex> ul(lock, std::try_to_lock);
31126ed5a4SJim Walker    if (!ul.owns_lock()) {
32126ed5a4SJim Walker        // Make concurrent updates fail, in realiy there should only be one
33126ed5a4SJim Walker        // admin connection making changes.
34126ed5a4SJim Walker        return cb::engine_error(cb::engine_errc::temporary_failure,
35126ed5a4SJim Walker                                "Collections::Manager::update already locked");
36126ed5a4SJim Walker    }
37126ed5a4SJim Walker
38126ed5a4SJim Walker    std::unique_ptr<Manifest> newManifest;
39126ed5a4SJim Walker    // Construct a newManifest (will throw if JSON was illegal)
40126ed5a4SJim Walker    try {
416e741613SJim Walker        newManifest =
426e741613SJim Walker                std::make_unique<Manifest>(json,
436e741613SJim Walker                                           bucket.getEPEngine()
446e741613SJim Walker                                                   .getConfiguration()
456e741613SJim Walker                                                   .getCollectionsMaxSize());
46126ed5a4SJim Walker    } catch (std::exception& e) {
47126ed5a4SJim Walker        LOG(EXTENSION_LOG_NOTICE,
48126ed5a4SJim Walker            "Collections::Manager::update can't construct manifest e.what:%s",
49126ed5a4SJim Walker            e.what());
50126ed5a4SJim Walker        return cb::engine_error(
51126ed5a4SJim Walker                cb::engine_errc::invalid_arguments,
52126ed5a4SJim Walker                "Collections::Manager::update manifest json invalid:" + json);
53126ed5a4SJim Walker    }
54126ed5a4SJim Walker
55126ed5a4SJim Walker    current = std::move(newManifest);
56126ed5a4SJim Walker
57126ed5a4SJim Walker    for (int i = 0; i < bucket.getVBuckets().getSize(); i++) {
58126ed5a4SJim Walker        auto vb = bucket.getVBuckets().getBucket(i);
59126ed5a4SJim Walker
60126ed5a4SJim Walker        if (vb && vb->getState() == vbucket_state_active) {
61126ed5a4SJim Walker            vb->updateFromManifest(*current);
62126ed5a4SJim Walker        }
63126ed5a4SJim Walker    }
64126ed5a4SJim Walker
65126ed5a4SJim Walker    return cb::engine_error(cb::engine_errc::success,
66126ed5a4SJim Walker                            "Collections::Manager::update");
67126ed5a4SJim Walker}
68126ed5a4SJim Walker
69901d1ab8SJim Walkercb::EngineErrorStringPair Collections::Manager::getManifest() const {
70901d1ab8SJim Walker    std::unique_lock<std::mutex> ul(lock);
7113069eacSJim Walker    if (current) {
7213069eacSJim Walker        return {cb::engine_errc::success, current->toJson()};
7313069eacSJim Walker    } else {
7413069eacSJim Walker        return {cb::engine_errc::no_collections_manifest, {}};
7513069eacSJim Walker    }
76901d1ab8SJim Walker}
77901d1ab8SJim Walker
78126ed5a4SJim Walkervoid Collections::Manager::update(VBucket& vb) const {
79126ed5a4SJim Walker    // Lock manager updates
80126ed5a4SJim Walker    std::lock_guard<std::mutex> ul(lock);
81d0d71f8aSJim Walker    if (current) {
82d0d71f8aSJim Walker        vb.updateFromManifest(*current);
83d0d71f8aSJim Walker    }
84126ed5a4SJim Walker}
8514fcb066SJim Walker
86d0d71f8aSJim WalkerCollections::Filter Collections::Manager::makeFilter(
87d0d71f8aSJim Walker        uint32_t openFlags, cb::const_byte_buffer jsonExtra) const {
8814fcb066SJim Walker    // Lock manager updates
8914fcb066SJim Walker    std::lock_guard<std::mutex> lg(lock);
9014fcb066SJim Walker    boost::optional<const std::string&> jsonFilter;
91d0d71f8aSJim Walker    std::string json;
92d0d71f8aSJim Walker    if (openFlags & DCP_OPEN_COLLECTIONS) {
93d0d71f8aSJim Walker        // assign to std::string as cJSON needs guaranteed zero termination
94d0d71f8aSJim Walker        json.assign(reinterpret_cast<const char*>(jsonExtra.data()),
95d0d71f8aSJim Walker                    jsonExtra.size());
9614fcb066SJim Walker        jsonFilter = json;
9714fcb066SJim Walker    }
98d0d71f8aSJim Walker    return Collections::Filter(jsonFilter, current.get());
9914fcb066SJim Walker}
1009e4dcf1fSJim Walker
1019e4dcf1fSJim Walker// This method is really to aid development and allow the dumping of the VB
1029e4dcf1fSJim Walker// collection data to the logs.
1039e4dcf1fSJim Walkervoid Collections::Manager::logAll(KVBucket& bucket) const {
1049e4dcf1fSJim Walker    std::stringstream ss;
1059e4dcf1fSJim Walker    ss << *this;
1069e4dcf1fSJim Walker    LOG(EXTENSION_LOG_NOTICE, "%s", ss.str().c_str());
1079e4dcf1fSJim Walker    for (int i = 0; i < bucket.getVBuckets().getSize(); i++) {
1089e4dcf1fSJim Walker        auto vb = bucket.getVBuckets().getBucket(i);
1099e4dcf1fSJim Walker        if (vb) {
1109e4dcf1fSJim Walker            std::stringstream vbss;
1119e4dcf1fSJim Walker            vbss << vb->lockCollections();
1129e4dcf1fSJim Walker            LOG(EXTENSION_LOG_NOTICE,
1139e4dcf1fSJim Walker                "vb:%d: %s %s",
1149e4dcf1fSJim Walker                i,
1159e4dcf1fSJim Walker                VBucket::toString(vb->getState()),
1169e4dcf1fSJim Walker                vbss.str().c_str());
1179e4dcf1fSJim Walker        }
1189e4dcf1fSJim Walker    }
1199e4dcf1fSJim Walker}
1209e4dcf1fSJim Walker
1219e4dcf1fSJim Walkervoid Collections::Manager::dump() const {
1229e4dcf1fSJim Walker    std::cerr << *this;
1239e4dcf1fSJim Walker}
1249e4dcf1fSJim Walker
1259e4dcf1fSJim Walkerstd::ostream& Collections::operator<<(std::ostream& os,
1269e4dcf1fSJim Walker                                      const Collections::Manager& manager) {
1279e4dcf1fSJim Walker    std::lock_guard<std::mutex> lg(manager.lock);
1289e4dcf1fSJim Walker    if (manager.current) {
1299e4dcf1fSJim Walker        os << "Collections::Manager current:" << *manager.current << "\n";
1309e4dcf1fSJim Walker    } else {
1319e4dcf1fSJim Walker        os << "Collections::Manager current:nullptr\n";
1329e4dcf1fSJim Walker    }
1339e4dcf1fSJim Walker    return os;
1349e4dcf1fSJim Walker}