Skip to content

Commit 87307c7

Browse files
committed
SERVER-41854 Add meta projection $shardName.
1 parent d9c06a3 commit 87307c7

21 files changed

+517
-20
lines changed

src/mongo/db/SConscript

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,8 @@ env.Library(
985985
'exec/requires_index_stage.cpp',
986986
'exec/shard_filter.cpp',
987987
'exec/shard_filterer_impl.cpp',
988+
'exec/shard_name.cpp',
989+
'exec/shard_namer_impl.cpp',
988990
'exec/skip.cpp',
989991
'exec/sort.cpp',
990992
'exec/sort_key_generator.cpp',

src/mongo/db/exec/projection.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ double textScore(const WorkingSetMember& member) {
8080
return 0.0;
8181
}
8282

83+
StringData shardName(const WorkingSetMember& member) {
84+
if (member.hasComputed(WSM_COMPUTED_SHARD_NAME)) {
85+
StringData shardName = static_cast<const ShardNameComputedData*>(
86+
member.getComputed(WSM_COMPUTED_SHARD_NAME))
87+
->getShardName();
88+
return shardName;
89+
}
90+
// It is permitted to request a shardName when none has been computed. Empty string is returned
91+
// as an empty value in this case.
92+
else
93+
return StringData("");
94+
}
95+
8396
void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
8497
member->keyData.clear();
8598
member->recordId = RecordId();
@@ -104,7 +117,8 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
104117
exec.needsSortKey() ? sortKey(member) : BSONObj(),
105118
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
106119
: boost::none,
107-
member.recordId.repr())
120+
member.recordId.repr(),
121+
exec.needsShardName() ? shardName(member) : StringData(""))
108122
: exec.projectCovered(
109123
member.keyData,
110124
exec.needsGeoNearDistance() ? boost::optional<const double>(geoDistance(member))
@@ -113,7 +127,8 @@ StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
113127
exec.needsSortKey() ? sortKey(member) : BSONObj(),
114128
exec.needsTextScore() ? boost::optional<const double>(textScore(member))
115129
: boost::none,
116-
member.recordId.repr());
130+
member.recordId.repr(),
131+
exec.needsShardName() ? shardName(member) : StringData(""));
117132
}
118133
} // namespace
119134

src/mongo/db/exec/projection_exec.cpp

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

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

241245
StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDatum>& keyData,
242246
const boost::optional<const double> geoDistance,
243247
const BSONObj& geoNearPoint,
244248
const BSONObj& sortKey,
245249
const boost::optional<const double> textScore,
246-
const int64_t recordId) const {
250+
const int64_t recordId,
251+
const StringData& shardName) const {
247252
invariant(!_include);
248253
BSONObjBuilder bob;
249254
// Go field by field.
@@ -288,15 +293,16 @@ StatusWith<BSONObj> ProjectionExec::projectCovered(const std::vector<IndexKeyDat
288293
}
289294

290295
bob.appendElements(projectedDoc.getObject());
291-
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId)};
296+
return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId, shardName)};
292297
}
293298

294299
BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
295300
const boost::optional<const double> geoDistance,
296301
const BSONObj& geoNearPoint,
297302
const BSONObj& sortKey,
298303
const boost::optional<const double> textScore,
299-
const int64_t recordId) const {
304+
const int64_t recordId,
305+
const StringData& shardName) const {
300306
for (MetaMap::const_iterator it = _meta.begin(); it != _meta.end(); ++it) {
301307
switch (it->second) {
302308
case META_GEONEAR_DIST:
@@ -322,6 +328,10 @@ BSONObj ProjectionExec::addMeta(BSONObjBuilder bob,
322328
bob.append(it->first, sortKey);
323329
break;
324330
}
331+
case META_SHARD_NAME: {
332+
bob.append(it->first, shardName);
333+
break;
334+
}
325335
case META_RECORDID:
326336
invariant(recordId != 0);
327337
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
@@ -62,6 +62,7 @@ class ProjectionExec {
6262
META_RECORDID,
6363
META_SORT_KEY,
6464
META_TEXT_SCORE,
65+
META_SHARD_NAME,
6566
};
6667

6768
/**
@@ -115,6 +116,13 @@ class ProjectionExec {
115116
return _needsTextScore;
116117
}
117118

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

142151
/**
143152
* Performs a projection given index 'KeyData' to directly retrieve results. This function
@@ -150,7 +159,8 @@ class ProjectionExec {
150159
const BSONObj& geoNearPoint = BSONObj(),
151160
const BSONObj& sortKey = BSONObj(),
152161
const boost::optional<const double> textScore = boost::none,
153-
const int64_t recordId = 0) const;
162+
const int64_t recordId = 0,
163+
const StringData& shardName = StringData("")) const;
154164

155165
/**
156166
* Determines if calls to the project method require that this object was created with the full
@@ -170,7 +180,8 @@ class ProjectionExec {
170180
const BSONObj& geoNearPoint,
171181
const BSONObj& sortKey,
172182
const boost::optional<const double> textScore,
173-
const int64_t recordId) const;
183+
const int64_t recordId,
184+
const StringData& shardName) const;
174185

175186
//
176187
// Initialization
@@ -268,6 +279,7 @@ class ProjectionExec {
268279
bool _needsGeoNearDistance = false;
269280
bool _needsGeoNearPoint = false;
270281
bool _needsTextScore = false;
282+
bool _needsShardName = false;
271283

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

src/mongo/db/exec/shard_name.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
31+
32+
#include "mongo/platform/basic.h"
33+
34+
#include "mongo/db/exec/shard_name.h"
35+
36+
#include <memory>
37+
38+
#include "mongo/db/exec/filter.h"
39+
#include "mongo/db/exec/scoped_timer.h"
40+
#include "mongo/db/exec/working_set_common.h"
41+
#include "mongo/db/exec/working_set_computed_data.h"
42+
#include "mongo/s/shard_key_pattern.h"
43+
#include "mongo/util/log.h"
44+
45+
namespace mongo {
46+
47+
using std::shared_ptr;
48+
using std::unique_ptr;
49+
using std::vector;
50+
using std::string;
51+
52+
// static
53+
const char* ShardNameStage::kStageType = "SHARD_NAME";
54+
55+
ShardNameStage:: ShardNameStage(OperationContext* opCtx,
56+
ScopedCollectionMetadata metadata,
57+
WorkingSet* ws,
58+
PlanStage* child)
59+
: PlanStage(kStageType, opCtx), _ws(ws), _shardNamer(std::move(metadata)) {
60+
_children.emplace_back(child);
61+
}
62+
63+
ShardNameStage::~ShardNameStage() {}
64+
65+
bool ShardNameStage::isEOF() {
66+
return child()->isEOF();
67+
}
68+
69+
PlanStage::StageState ShardNameStage::doWork(WorkingSetID* out) {
70+
// If we've returned as many results as we're limited to, isEOF will be true.
71+
if (isEOF()) {
72+
return PlanStage::IS_EOF;
73+
}
74+
75+
StageState status = child()->work(out);
76+
77+
if (PlanStage::ADVANCED == status) {
78+
// If we're sharded make sure to add shardName to the output.
79+
if (_shardNamer.isCollectionSharded()) {
80+
WorkingSetMember* member = _ws->get(*out);
81+
const StringData shardName = _shardNamer.shardName();
82+
83+
// Populate the working set member with the shard name and return it.
84+
member->addComputed(new ShardNameComputedData(shardName));
85+
}
86+
87+
// If we're here either we have shard state and added the shardName, or we have no shard
88+
// state. Either way, we advance.
89+
return status;
90+
}
91+
92+
return status;
93+
}
94+
95+
unique_ptr<PlanStageStats> ShardNameStage::getStats() {
96+
_commonStats.isEOF = isEOF();
97+
unique_ptr<PlanStageStats> ret =
98+
std::make_unique<PlanStageStats>(_commonStats, STAGE_SHARD_NAME);
99+
ret->children.emplace_back(child()->getStats());
100+
return ret;
101+
}
102+
103+
const SpecificStats* ShardNameStage::getSpecificStats() const {
104+
// No specific stats are tracked for the shard name stage.
105+
return nullptr;
106+
}
107+
108+
} // 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)