1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/platform/basic.h"
32 
33 #include "mongo/db/keys_collection_cache_reader.h"
34 
35 #include "mongo/db/keys_collection_client.h"
36 #include "mongo/db/keys_collection_document.h"
37 #include "mongo/util/mongoutils/str.h"
38 
39 namespace mongo {
40 
KeysCollectionCacheReader(std::string purpose,KeysCollectionClient * client)41 KeysCollectionCacheReader::KeysCollectionCacheReader(std::string purpose,
42                                                      KeysCollectionClient* client)
43     : _purpose(std::move(purpose)), _client(client) {}
44 
refresh(OperationContext * opCtx)45 StatusWith<KeysCollectionDocument> KeysCollectionCacheReader::refresh(OperationContext* opCtx) {
46     LogicalTime newerThanThis;
47 
48     decltype(_cache)::size_type originalSize = 0;
49 
50     {
51         stdx::lock_guard<stdx::mutex> lk(_cacheMutex);
52         auto iter = _cache.crbegin();
53         if (iter != _cache.crend()) {
54             newerThanThis = iter->second.getExpiresAt();
55         }
56 
57         originalSize = _cache.size();
58     }
59 
60     auto refreshStatus = _client->getNewKeys(opCtx, _purpose, newerThanThis);
61 
62     if (!refreshStatus.isOK()) {
63         return refreshStatus.getStatus();
64     }
65 
66     auto& newKeys = refreshStatus.getValue();
67 
68     stdx::lock_guard<stdx::mutex> lk(_cacheMutex);
69     if (originalSize > _cache.size()) {
70         // _cache cleared while we getting the new keys, just return the newest key without
71         // touching the _cache so the next refresh will populate it properly.
72         // Note: newKeys are sorted.
73         if (!newKeys.empty()) {
74             return std::move(newKeys.back());
75         }
76     }
77 
78     for (auto&& key : newKeys) {
79         _cache.emplace(std::make_pair(key.getExpiresAt(), std::move(key)));
80     }
81 
82     if (_cache.empty()) {
83         return {ErrorCodes::KeyNotFound, "No keys found after refresh"};
84     }
85 
86     return _cache.crbegin()->second;
87 }
88 
getKeyById(long long keyId,const LogicalTime & forThisTime)89 StatusWith<KeysCollectionDocument> KeysCollectionCacheReader::getKeyById(
90     long long keyId, const LogicalTime& forThisTime) {
91     stdx::lock_guard<stdx::mutex> lk(_cacheMutex);
92 
93     for (auto iter = _cache.lower_bound(forThisTime); iter != _cache.cend(); ++iter) {
94         if (iter->second.getKeyId() == keyId) {
95             return iter->second;
96         }
97     }
98 
99     return {ErrorCodes::KeyNotFound,
100             str::stream() << "Cache Reader No keys found for " << _purpose
101                           << " that is valid for time: "
102                           << forThisTime.toString()
103                           << " with id: "
104                           << keyId};
105 }
106 
getKey(const LogicalTime & forThisTime)107 StatusWith<KeysCollectionDocument> KeysCollectionCacheReader::getKey(
108     const LogicalTime& forThisTime) {
109     stdx::lock_guard<stdx::mutex> lk(_cacheMutex);
110 
111     auto iter = _cache.upper_bound(forThisTime);
112 
113     if (iter == _cache.cend()) {
114         return {ErrorCodes::KeyNotFound,
115                 str::stream() << "No key found that is valid for " << forThisTime.toString()};
116     }
117 
118     return iter->second;
119 }
120 
resetCache()121 void KeysCollectionCacheReader::resetCache() {
122     // keys that read with non majority readConcern level can be rolled back.
123     if (!_client->supportsMajorityReads()) {
124         stdx::lock_guard<stdx::mutex> lk(_cacheMutex);
125         _cache.clear();
126     }
127 }
128 
129 }  // namespace mongo
130