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 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding
32 
33 #include "mongo/platform/basic.h"
34 
35 #include <string>
36 
37 #include "mongo/base/status.h"
38 #include "mongo/db/audit.h"
39 #include "mongo/db/service_context.h"
40 #include "mongo/rpc/metadata/client_metadata_ismaster.h"
41 #include "mongo/rpc/metadata/config_server_metadata.h"
42 #include "mongo/rpc/metadata/metadata_hook.h"
43 #include "mongo/rpc/metadata/repl_set_metadata.h"
44 #include "mongo/s/client/shard_registry.h"
45 #include "mongo/s/grid.h"
46 #include "mongo/s/sharding_egress_metadata_hook.h"
47 #include "mongo/util/net/hostandport.h"
48 
49 namespace mongo {
50 namespace rpc {
51 
ShardingEgressMetadataHook(ServiceContext * serviceContext)52 ShardingEgressMetadataHook::ShardingEgressMetadataHook(ServiceContext* serviceContext)
53     : _serviceContext(serviceContext) {
54     invariant(_serviceContext);
55 }
56 
writeRequestMetadata(OperationContext * opCtx,BSONObjBuilder * metadataBob)57 Status ShardingEgressMetadataHook::writeRequestMetadata(OperationContext* opCtx,
58                                                         BSONObjBuilder* metadataBob) {
59     try {
60         audit::writeImpersonatedUsersToMetadata(opCtx, metadataBob);
61         ClientMetadataIsMasterState::writeToMetadata(opCtx, metadataBob);
62         rpc::ConfigServerMetadata(_getConfigServerOpTime()).writeToMetadata(metadataBob);
63         return Status::OK();
64     } catch (...) {
65         return exceptionToStatus();
66     }
67 }
68 
readReplyMetadata(OperationContext * opCtx,StringData replySource,const BSONObj & metadataObj)69 Status ShardingEgressMetadataHook::readReplyMetadata(OperationContext* opCtx,
70                                                      StringData replySource,
71                                                      const BSONObj& metadataObj) {
72     try {
73         _saveGLEStats(metadataObj, replySource);
74         return _advanceConfigOpTimeFromShard(opCtx, replySource.toString(), metadataObj);
75     } catch (...) {
76         return exceptionToStatus();
77     }
78 }
79 
_advanceConfigOpTimeFromShard(OperationContext * opCtx,const ShardId & shardId,const BSONObj & metadataObj)80 Status ShardingEgressMetadataHook::_advanceConfigOpTimeFromShard(OperationContext* opCtx,
81                                                                  const ShardId& shardId,
82                                                                  const BSONObj& metadataObj) {
83     auto const grid = Grid::get(_serviceContext);
84 
85     try {
86         auto shard = grid->shardRegistry()->getShardNoReload(shardId);
87         if (!shard) {
88             return Status::OK();
89         }
90 
91         // Update our notion of the config server opTime from the configOpTime in the response.
92         if (shard->isConfig()) {
93             // Config servers return the config opTime as part of their own metadata.
94             if (metadataObj.hasField(rpc::kReplSetMetadataFieldName)) {
95                 auto parseStatus = rpc::ReplSetMetadata::readFromMetadata(metadataObj);
96                 if (!parseStatus.isOK()) {
97                     return parseStatus.getStatus();
98                 }
99 
100                 // Use the last committed optime to advance config optime.
101                 // For successful majority writes, we could use the optime of the last op
102                 // from us and lastOpCommitted is always greater than or equal to it.
103                 // On majority write failures, the last visible optime would be incorrect
104                 // due to rollback as explained in SERVER-24630 and the last committed optime
105                 // is safe to use.
106                 const auto& replMetadata = parseStatus.getValue();
107                 const auto opTime = replMetadata.getLastOpCommitted();
108                 grid->advanceConfigOpTime(opCtx, opTime, "reply from config server node");
109             }
110         } else {
111             // Regular shards return the config opTime as part of ConfigServerMetadata.
112             auto parseStatus = rpc::ConfigServerMetadata::readFromMetadata(metadataObj);
113             if (!parseStatus.isOK()) {
114                 return parseStatus.getStatus();
115             }
116 
117             const auto& configMetadata = parseStatus.getValue();
118             const auto opTime = configMetadata.getOpTime();
119             if (opTime.is_initialized()) {
120                 grid->advanceConfigOpTime(opCtx,
121                                           opTime.get(),
122                                           str::stream() << "reply from shard " << shardId
123                                                         << " node");
124             }
125         }
126         return Status::OK();
127     } catch (...) {
128         return exceptionToStatus();
129     }
130 }
131 
132 }  // namespace rpc
133 }  // namespace mongo
134