Skip to content

Commit f5ad625

Browse files
committed
SERVER-41854 Add meta projection $shardName.
1 parent 2cba3bf commit f5ad625

22 files changed

+529
-20
lines changed

src/mongo/db/SConscript

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,8 @@ env.Library(
992992
'exec/requires_index_stage.cpp',
993993
'exec/shard_filter.cpp',
994994
'exec/shard_filterer_impl.cpp',
995+
'exec/shard_name.cpp',
996+
'exec/shard_namer_impl.cpp',
995997
'exec/skip.cpp',
996998
'exec/sort.cpp',
997999
'exec/sort_key_generator.cpp',

src/mongo/db/exec/projection.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ double textScore(const WorkingSetMember& member) {
7777
}
7878
}
7979

80+
StringData shardName(const WorkingSetMember& member) {
81+
auto&& metadata = member.metadata();
82+
if (metadata.hasShardName()) {
83+
return metadata.getShardName();
84+
}
85+
else {
86+
// It is permitted to request a shardName when none has been computed. Empty string is returned
87+
// as an empty value in this case.
88+
return StringData("");
89+
}
90+
}
91+
8092
void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
8193
member->keyData.clear();
8294
member->recordId = RecordId();
@@ -101,7 +113,8 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
101113
exec.needsSortKey() ? sortKey(member) : BSONObj(),
102114
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
103115
: boost::none,
104-
member.recordId.repr())
116+
member.recordId.repr(),
117+
exec.needsShardName() ? shardName(member) : StringData(""))
105118
: exec.projectCovered(
106119
member.keyData,
107120
exec.needsGeoNearDistance() ? boost::optional<const double>(geoDistance(member))
@@ -110,7 +123,8 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
110123
exec.needsSortKey() ? sortKey(member) : BSONObj(),
111124
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
112125
: boost::none,
113-
member.recordId.repr());
126+
member.recordId.repr(),
127+
exec.needsShardName() ? shardName(member) : StringData(""));
114128
}
115129
} // namespace
116130

src/mongo/db/exec/projection_exec.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ ProjectionExec::ProjectionExec(OperationContext* opCtx,
119119
_needsGeoNearDistance = true;
120120
} else if (e2.valuestr() == QueryRequest::metaIndexKey) {
121121
_hasReturnKey = true;
122+
} else if (e2.valuestr() == QueryRequest::metaShardName) {
123+
_meta[e.fieldName()] = META_SHARD_NAME;
124+
_needsShardName = true;
122125
} else {
123126
// This shouldn't happen, should be caught by parsing.
124127
MONGO_UNREACHABLE;
@@ -221,7 +224,8 @@ StatusWith<BSONObj> ProjectionExec::project(const BSONObj& in,
221224
Value geoNearPoint,
222225
const BSONObj& sortKey,
223226
const boost::optional<const double> textScore,
224-
const int64_t recordId) const {
227+
const int64_t recordId,
228+
const StringData& shardName) const {
225229
BSONObjBuilder bob;
226230
MatchDetails matchDetails;
227231

@@ -236,15 +240,16 @@ StatusWith<BSONObj> ProjectionExec::project(const BSONObj& in,
236240
if (!projStatus.isOK())
237241
return projStatus;
238242
else
239-
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId)};
243+
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId, shardName)};
240244
}
241245

242246
StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDatum>& keyData,
243247
const boost::optional<const double> geoDistance,
244248
Value geoNearPoint,
245249
const BSONObj& sortKey,
246250
const boost::optional<const double> textScore,
247-
const int64_t recordId) const {
251+
const int64_t recordId,
252+
const StringData& shardName) const {
248253
invariant(!_include);
249254
BSONObjBuilder bob;
250255
// Go field by field.
@@ -289,15 +294,16 @@ StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDat
289294
}
290295

291296
bob.appendElements(projectedDoc.getObject());
292-
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId)};
297+
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId, shardName)};
293298
}
294299

295300
BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
296301
const boost::optional<const double> geoDistance,
297302
Value geoNearPoint,
298303
const BSONObj& sortKey,
299304
const boost::optional<const double> textScore,
300-
const int64_t recordId) const {
305+
const int64_t recordId,
306+
const StringData& shardName) const {
301307
for (MetaMap::const_iterator it = _meta.begin(); it != _meta.end(); ++it) {
302308
switch (it->second) {
303309
case META_GEONEAR_DIST:
@@ -318,6 +324,10 @@ BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
318324
bob.append(it->first, sortKey);
319325
break;
320326
}
327+
case META_SHARD_NAME: {
328+
bob.append(it->first, shardName);
329+
break;
330+
}
321331
case META_RECORDID:
322332
invariant(recordId != 0);
323333
bob.append(it->first, recordId);

src/mongo/db/exec/projection_exec.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class ProjectionExec {
6363
META_RECORDID,
6464
META_SORT_KEY,
6565
META_TEXT_SCORE,
66+
META_SHARD_NAME,
6667
};
6768

6869
/**
@@ -116,6 +117,13 @@ class ProjectionExec {
116117
return _needsTextScore;
117118
}
118119

120+
/**
121+
* Indicates whether 'shardName' is going to be used in 'project()'.
122+
*/
123+
bool needsShardName() const {
124+
return _needsShardName;
125+
}
126+
119127
/**
120128
* Returns false if there are no meta fields to project.
121129
*/
@@ -138,7 +146,8 @@ class ProjectionExec {
138146
Value geoNearPoint = Value{},
139147
const BSONObj& sortKey = BSONObj(),
140148
const boost::optional<const double> textScore = boost::none,
141-
const int64_t recordId = 0) const;
149+
const int64_t recordId = 0,
150+
const StringData& shardName = StringData("")) const;
142151

143152
/**
144153
* Performs a projection given index 'KeyData' to directly retrieve results. This function
@@ -151,7 +160,8 @@ class ProjectionExec {
151160
Value geoNearPoint = Value{},
152161
const BSONObj& sortKey = BSONObj(),
153162
const boost::optional<const double> textScore = boost::none,
154-
const int64_t recordId = 0) const;
163+
const int64_t recordId = 0,
164+
const StringData& shardName = StringData("")) const;
155165

156166
/**
157167
* Determines if calls to the project method require that this object was created with the full
@@ -171,7 +181,8 @@ class ProjectionExec {
171181
Value geoNearPoint,
172182
const BSONObj& sortKey,
173183
const boost::optional<const double> textScore,
174-
const int64_t recordId) const;
184+
const int64_t recordId,
185+
const StringData& shardName) const;
175186

176187
//
177188
// Initialization
@@ -269,6 +280,7 @@ class ProjectionExec {
269280
bool _needsGeoNearDistance = false;
270281
bool _needsGeoNearPoint = false;
271282
bool _needsTextScore = false;
283+
bool _needsShardName = false;
272284

273285
// The field names associated with any sortKey meta-projection(s). Empty if there is no sortKey
274286
// meta-projection.

src/mongo/db/exec/shard_name.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* Copyright (C) 2019-present MongoDB, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*
17+
* As a special exception, the copyright holders give permission to link the
18+
* code of portions of this program with the OpenSSL library under certain
19+
* conditions as described in each individual source file and distribute
20+
* linked combinations including the program with the OpenSSL library. You
21+
* must comply with the Server Side Public License in all respects for
22+
* all of the code used other than as permitted herein. If you modify file(s)
23+
* with this exception, you may extend this exception to your version of the
24+
* file(s), but you are not obligated to do so. If you do not wish to do so,
25+
* delete this exception statement from your version. If you delete this
26+
* exception statement from all source files in the program, then also delete
27+
* it in the license file.
28+
*/
29+
30+
#include "mongo/platform/basic.h"
31+
32+
#include "mongo/db/exec/shard_name.h"
33+
34+
#include <memory>
35+
36+
#include "mongo/db/exec/filter.h"
37+
#include "mongo/db/exec/scoped_timer.h"
38+
#include "mongo/db/exec/working_set_common.h"
39+
#include "mongo/s/shard_key_pattern.h"
40+
41+
namespace mongo {
42+
43+
using std::shared_ptr;
44+
using std::unique_ptr;
45+
using std::vector;
46+
using std::string;
47+
48+
// static
49+
const char* ShardNameStage::kStageType = "SHARD_NAME";
50+
51+
ShardNameStage::ShardNameStage(OperationContext* opCtx,
52+
ScopedCollectionMetadata metadata,
53+
WorkingSet* ws,
54+
PlanStage* child)
55+
: PlanStage(kStageType, opCtx), _ws(ws), _shardNamer(std::move(metadata)) {
56+
_children.emplace_back(child);
57+
}
58+
59+
ShardNameStage::~ShardNameStage() {}
60+
61+
bool ShardNameStage::isEOF() {
62+
return child()->isEOF();
63+
}
64+
65+
PlanStage::StageState ShardNameStage::doWork(WorkingSetID* out) {
66+
// If we've returned as many results as we're limited to, isEOF will be true.
67+
if (isEOF()) {
68+
return PlanStage::IS_EOF;
69+
}
70+
71+
StageState status = child()->work(out);
72+
73+
if (PlanStage::ADVANCED == status) {
74+
// If we're sharded make sure to add shardName to the output.
75+
if (_shardNamer.isCollectionSharded()) {
76+
WorkingSetMember* member = _ws->get(*out);
77+
const StringData shardName = _shardNamer.shardName();
78+
79+
// Populate the working set member with the shard name and return it.
80+
member->metadata().setShardName(shardName);
81+
}
82+
83+
// If we're here either we have shard state and added the shardName, or we have no shard
84+
// state. Either way, we advance.
85+
return status;
86+
}
87+
88+
return status;
89+
}
90+
91+
unique_ptr<PlanStageStats> ShardNameStage::getStats() {
92+
_commonStats.isEOF = isEOF();
93+
unique_ptr<PlanStageStats> ret =
94+
std::make_unique<PlanStageStats>(_commonStats, STAGE_SHARD_NAME);
95+
ret->children.emplace_back(child()->getStats());
96+
return ret;
97+
}
98+
99+
const SpecificStats* ShardNameStage::getSpecificStats() const {
100+
// No specific stats are tracked for the shard name stage.
101+
return nullptr;
102+
}
103+
104+
} // namespace mongo

src/mongo/db/exec/shard_name.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright (C) 2019-present MongoDB, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*
17+
* As a special exception, the copyright holders give permission to link the
18+
* code of portions of this program with the OpenSSL library under certain
19+
* conditions as described in each individual source file and distribute
20+
* linked combinations including the program with the OpenSSL library. You
21+
* must comply with the Server Side Public License in all respects for
22+
* all of the code used other than as permitted herein. If you modify file(s)
23+
* with this exception, you may extend this exception to your version of the
24+
* file(s), but you are not obligated to do so. If you do not wish to do so,
25+
* delete this exception statement from your version. If you delete this
26+
* exception statement from all source files in the program, then also delete
27+
* it in the license file.
28+
*/
29+
30+
#pragma once
31+
32+
#include "mongo/db/exec/plan_stage.h"
33+
#include "mongo/db/exec/shard_namer_impl.h"
34+
35+
namespace mongo {
36+
37+
class ShardNameStage final : public PlanStage {
38+
public:
39+
ShardNameStage(OperationContext* opCtx,
40+
ScopedCollectionMetadata metadata,
41+
WorkingSet* ws,
42+
PlanStage* child);
43+
~ShardNameStage();
44+
45+
bool isEOF() final;
46+
StageState doWork(WorkingSetID* out) final;
47+
48+
StageType stageType() const final {
49+
return STAGE_SHARD_NAME;
50+
}
51+
52+
std::unique_ptr<PlanStageStats> getStats() final;
53+
54+
const SpecificStats* getSpecificStats() const final;
55+
56+
static const char* kStageType;
57+
58+
private:
59+
WorkingSet* _ws;
60+
61+
// Note: it is important that this owns the ScopedCollectionMetadata from the time this stage
62+
// is constructed. See ScopedCollectionMetadata class comment and MetadataManager comment for
63+
// details. The existence of the ScopedCollectionMetadata prevents data which may have been
64+
// migrated from being deleted while the query is still active. If we didn't hold one
65+
// ScopedCollectionMetadata for the entire query, it'd be possible for data which the query
66+
// needs to read to be deleted while it's still running.
67+
ShardNamerImpl _shardNamer;
68+
};
69+
70+
} // namespace mongo

0 commit comments

Comments
 (0)