1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
// @file queryoptimizercursor.h
/**
* Copyright (C) 2011 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace mongo {
/** Helper class for caching and counting matches during execution of a QueryPlan. */
class CachedMatchCounter {
public:
/**
* @param aggregateNscanned - shared count of nscanned for this and othe plans.
* @param cumulativeCount - starting point for accumulated count over a series of plans.
*/
CachedMatchCounter( long long &aggregateNscanned, int cumulativeCount ) : _aggregateNscanned( aggregateNscanned ), _nscanned(), _cumulativeCount( cumulativeCount ), _count(), _checkDups(), _match( Unknown ), _counted() {}
/** Set whether dup checking is enabled when counting. */
void setCheckDups( bool checkDups ) { _checkDups = checkDups; }
/**
* Usual sequence of events:
* 1) resetMatch() - reset stored match value to Unkonwn.
* 2) setMatch() - set match value to a definite true/false value.
* 3) knowMatch() - check if setMatch() has been called.
* 4) countMatch() - increment count if match is true.
*/
void resetMatch() {
_match = Unknown;
_counted = false;
}
void setMatch( bool match ) { _match = match ? True : False; }
bool knowMatch() const { return _match != Unknown; }
void countMatch( const DiskLoc &loc ) {
if ( !_counted && _match == True && !getsetdup( loc ) ) {
++_cumulativeCount;
++_count;
_counted = true;
}
}
bool enoughCumulativeMatchesToChooseAPlan() const {
// This is equivalent to the default condition for switching from
// a query to a getMore, which was the historical default match count for
// choosing a plan.
return _cumulativeCount >= 101;
}
bool enoughMatchesToRecordPlan() const {
// Recording after 50 matches is a historical default (101 default limit / 2).
return _count > 50;
}
int cumulativeCount() const { return _cumulativeCount; }
int count() const { return _count; }
/** Update local and aggregate nscanned counts. */
void updateNscanned( long long nscanned ) {
_aggregateNscanned += ( nscanned - _nscanned );
_nscanned = nscanned;
}
long long nscanned() const { return _nscanned; }
long long &aggregateNscanned() const { return _aggregateNscanned; }
private:
bool getsetdup( const DiskLoc &loc ) {
if ( !_checkDups ) {
return false;
}
pair<set<DiskLoc>::iterator, bool> p = _dups.insert( loc );
return !p.second;
}
long long &_aggregateNscanned;
long long _nscanned;
int _cumulativeCount;
int _count;
bool _checkDups;
enum MatchState { Unknown, False, True };
MatchState _match;
bool _counted;
set<DiskLoc> _dups;
};
/** Dup tracking class, optimizing one common case with small set and few initial reads. */
class SmallDupSet {
public:
SmallDupSet() : _accesses() {
_vec.reserve( 250 );
}
/** @return true if @param 'loc' already added to the set, false if adding to the set in this call. */
bool getsetdup( const DiskLoc &loc ) {
access();
return vec() ? getsetdupVec( loc ) : getsetdupSet( loc );
}
/** @return true when @param loc in the set. */
bool getdup( const DiskLoc &loc ) {
access();
return vec() ? getdupVec( loc ) : getdupSet( loc );
}
private:
void access() {
++_accesses;
mayUpgrade();
}
void mayUpgrade() {
if ( vec() && _accesses > 500 ) {
_set.insert( _vec.begin(), _vec.end() );
}
}
bool vec() const {
return _set.size() == 0;
}
bool getsetdupVec( const DiskLoc &loc ) {
if ( getdupVec( loc ) ) {
return true;
}
_vec.push_back( loc );
return false;
}
bool getdupVec( const DiskLoc &loc ) const {
for( vector<DiskLoc>::const_iterator i = _vec.begin(); i != _vec.end(); ++i ) {
if ( *i == loc ) {
return true;
}
}
return false;
}
bool getsetdupSet( const DiskLoc &loc ) {
pair<set<DiskLoc>::iterator, bool> p = _set.insert(loc);
return !p.second;
}
bool getdupSet( const DiskLoc &loc ) {
return _set.count( loc ) > 0;
}
vector<DiskLoc> _vec;
set<DiskLoc> _set;
long long _accesses;
};
} // namespace mongo
|