10000 Feature/aql subquery opt shadow row interface (#9987) · archerli/arangodb@265eb15 · GitHub
[go: up one dir, main page]

Skip to content

Commit 265eb15

Browse files
mchackigoedderz
andauthored
Feature/aql subquery opt shadow row interface (arangodb#9987)
* Added first draft of ShadowRow Interface * Refactored RegisterPlan and pulled it out of the ExecutionNode * AqlItemBlock now has an additional hidden register to store subquery depth infromation. Right now no shadow rows are created, however thy could now make use of this situation * Added API and test to insert new shadow rows into an OutputRow. * Extrated test helper function * Added API and tests to consume ShadowRows. Interface is there and compiles with templates, we might need to implement further underlying functions later on as we make progress on ShadowRows, it is only implemented in the minimal way (intentionally) * Added a test for nested ShadowRows and adapted OutputRow accordingly. * Added additional memory include, seems to be required under GCC not udner CLANG * it actually helps to save fies before commiting them... * Fixed serialization/Deserialization of AqlItemBlock to contain the hidden subquery register now * Added a c++ test for AqlItemBlock. It just covers the basics thus far and needs to be improved. * Fixed toVPack of AqlInputRow for subqueries. Also added serialization / deserialization tests for AqlItemRow * Added a c++ test for inputAqlItem row serialization => AqlItemBlock deserialization * Attempt to fix :windows: compile warning * Added a serialization format to be backwards compatible to 3.5 whenever AQL item blocks need to be send over to remote nodes. * Added more tests for serialization of shadow rows, and fixed a bug revealed by this * Apply suggestions from code review Thank you for review! Co-Authored-By: Tobias Gödderz <tobias@arangodb.com> * Review comments. Thanks for spotting * Fixed hidden merge conflicts * Attempt to explain windows on how one can actually cast numbers into other number types... * Update arangod/Aql/AqlItemBlockSerializationFormat.h Co-Authored-By: Tobias Gödderz <tobias@arangodb.com>
1 parent 6372823 commit 265eb15

File tree

78 files changed

+2535
-1185
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+2535
-1185
lines changed

arangod/Aql/AqlItemBlock.cpp

Lines changed: 122 additions & 109 deletions
Large diffs are not rendered by default.

arangod/Aql/AqlItemBlock.h

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace aql {
3737
class AqlItemBlockManager;
3838
class BlockCollector;
3939
class SharedAqlItemBlockPtr;
40+
enum class SerializationFormat;
4041

4142
// an <AqlItemBlock> is a <nrItems>x<nrRegs> vector of <AqlValue>s (not
4243
// pointers). The size of an <AqlItemBlock> is the number of items.
@@ -71,14 +72,16 @@ class AqlItemBlock {
7172

7273
void initFromSlice(arangodb::velocypack::Slice);
7374

75+
SerializationFormat getFormatType() const;
76+
7477
protected:
7578
/// @brief destroy the block
7679
/// Should only ever be deleted by AqlItemManager::returnBlock, so the
7780
/// destructor is protected.
7881
~AqlItemBlock() {
7982
TRI_ASSERT(_refCount == 0);
8083
destroy();
81-
decreaseMemoryUsage(sizeof(AqlValue) * _nrItems * _nrRegs);
84+
decreaseMemoryUsage(sizeof(AqlValue) * _nrItems * internalNrRegs());
8285
}
8386

8487
private:
@@ -97,23 +100,17 @@ class AqlItemBlock {
97100
public:
98101
/// @brief getValue, get the value of a register
99102
inline AqlValue getValue(size_t index, RegisterId varNr) const {
100-
TRI_ASSERT(index < _nrItems);
101-
TRI_ASSERT(varNr < _nrRegs);
102-
return _data[index * _nrRegs + varNr];
103+
return _data[getAddress(index, varNr)];
103104
}
104105

105106
/// @brief getValue, get the value of a register by reference
106107
inline AqlValue const& getValueReference(size_t index, RegisterId varNr) const {
107-
TRI_ASSERT(index < _nrItems);
108-
TRI_ASSERT(varNr < _nrRegs);
109-
return _data[index * _nrRegs + varNr];
108+
return _data[getAddress(index, varNr)];
110109
}
111110

112111
/// @brief setValue, set the current value of a register
113112
inline void setValue(size_t index, RegisterId varNr, AqlValue const& value) {
114-
TRI_ASSERT(index < _nrItems);
115-
TRI_ASSERT(varNr < _nrRegs);
116-
TRI_ASSERT(_data[index * _nrRegs + varNr].isEmpty());
113+
TRI_ASSERT(_data[getAddress(index, varNr)].isEmpty());
117114

118115
// First update the reference count, if this fails, the value is empty
119116
if (value.requiresDestruction()) {
@@ -123,27 +120,24 @@ class AqlItemBlock {
123120
}
124121
}
125122

126-
_data[index * _nrRegs + varNr] = value;
123+
_data[getAddress(index, varNr)] = value;
127124
}
128125

129126
/// @brief emplaceValue, set the current value of a register, constructing
130127
/// it in place
131128
template <typename... Args>
132-
//std::enable_if_t<!(std::is_same<AqlValue,std::decay_t<Args>>::value || ...), void>
133-
void
134-
emplaceValue(size_t index, RegisterId varNr, Args&&... args) {
135-
TRI_ASSERT(index < _nrItems);
136-
TRI_ASSERT(varNr < _nrRegs);
137-
138-
AqlValue* p = &_data[index * _nrRegs + varNr];
129+
// std::enable_if_t<!(std::is_same<AqlValue,std::decay_t<Args>>::value || ...), void>
130+
void emplaceValue(size_t index, RegisterId varNr, Args&&... args) {
131+
auto address = getAddress(index, varNr);
132+
AqlValue* p = &_data[address];
139133
TRI_ASSERT(p->isEmpty());
140134
// construct the AqlValue in place
141135
AqlValue* value;
142136
try {
143137
value = new (p) AqlValue(std::forward<Args>(args)...);
144138
} catch (...) {
145139
// clean up the cell
146-
_data[index * _nrRegs + varNr].erase();
140+
_data[address].erase();
147141
throw;
148142
}
149143

@@ -159,7 +153,7 @@ class AqlItemBlock {
159153
value->~AqlValue();
160154
// TODO - instead of disabling it completly we could you use
161155
// a constexpr if() with c++17
162-
_data[index * _nrRegs + varNr].destroy();
156+
_data[address].destroy();
163157
throw;
164158
}
165159
}
@@ -169,7 +163,7 @@ class AqlItemBlock {
169163
/// use with caution only in special situations when it can be ensured that
170164
/// no one else will be pointing to the same value
171165
void destroyValue(size_t index, RegisterId varNr) {
172-
auto& element = _data[index * _nrRegs + varNr];
166+
auto& element = _data[getAddress(index, varNr)];
173167

174168
if (element.requiresDestruction()) {
175169
auto it = _valueCount.find(element);
@@ -190,7 +184,7 @@ class AqlItemBlock {
190184
/// @brief eraseValue, erase the current value of a register not freeing it
191185
/// this is used if the value is stolen and later released from elsewhere
192186
void eraseValue(size_t index, RegisterId varNr) {
193-
auto& element = _data[index * _nrRegs + varNr];
187+
auto& element = _data[getAddress(index, varNr)];
194188

195189
if (element.requiresDestruction()) {
196190
auto it = _valueCount.find(element);
@@ -214,7 +208,7 @@ class AqlItemBlock {
214208
/// elsewhere
215209
void eraseAll() {
216210
for (size_t i = 0; i < numEntries(); i++) {
217-
auto &it = _data[i];
211+
auto& it = _data[i];
218212
if (!it.isEmpty()) {
219213
it.erase();
220214
}
@@ -245,20 +239,23 @@ class AqlItemBlock {
245239
TRI_ASSERT(currentRow != fromRow);
246240

247241
for (RegisterId i = 0; i < curRegs; i++) {
248-
if (_data[currentRow * _nrRegs + i].isEmpty()) {
242+
auto currentAddress = getAddress(currentRow, i);
243+
auto fromAddress = getAddress(fromRow, i);
244+
if (_data[currentAddress].isEmpty()) {
249245
// First update the reference count, if this fails, the value is empty
250-
if (_data[fromRow * _nrRegs + i].requiresDestruction()) {
251-
++_valueCount[_data[fromRow * _nrRegs + i]];
246+
if (_data[fromAddress].requiresDestruction()) {
247+
++_valueCount[_data[fromAddress]];
252248
}
253-
TRI_ASSERT(_data[currentRow * _nrRegs + i].isEmpty());
254-
_data[currentRow * _nrRegs + i] = _data[fromRow * _nrRegs + i];
249+
TRI_ASSERT(_data[currentAddress].isEmpty());
250+
_data[currentAddress] = _data[fromAddress];
255251
}
256252
}
253+
// Copy over subqueryDepth
254+
copySubqueryDepth(currentRow, fromRow);
257255
}
258256

259257
void copyValuesFromRow(size_t currentRow,
260-
std::unordered_set<RegisterId> const& regs,
261-
size_t fromRow) {
258+
std::unordered_set<RegisterId> const& regs, size_t fromRow) {
262259
TRI_ASSERT(currentRow != fromRow);
263260

264261
for (auto const reg : regs) {
@@ -268,9 +265,11 @@ class AqlItemBlock {
268265
if (getValueReference(fromRow, reg).requiresDestruction()) {
269266
++_valueCount[getValueReference(fromRow, reg)];
270267
}
271-
_data[currentRow * _nrRegs + reg] = getValueReference(fromRow, reg);
268+
_data[getAddress(currentRow, reg)] = getValueReference(fromRow, reg);
272269
}
273270
}
271+
// Copy over subqueryDepth
272+
copySubqueryDepth(currentRow, fromRow);
274273
}
275274

276275
/// @brief valueCount
@@ -302,14 +301,11 @@ class AqlItemBlock {
302301
/// @brief getter for _nrItems
303302
inline size_t size() const noexcept { return _nrItems; }
304303

305-
306304
/// @brief Number of entries in the matrix. If this changes, the memory usage
307305
/// must be / in- or decreased appropriately as well.
308306
/// All entries _data[i] for numEntries() <= i < _data.size() always have to
309307
/// be erased, i.e. empty / none!
310-
inline size_t numEntries() const {
311-
return _nrRegs * _nrItems;
312-
}
308+
inline size_t numEntries() const { return internalNrRegs() * _nrItems; }
313309

314310
inline size_t capacity() const noexcept { return _data.capacity(); }
315311

@@ -349,6 +345,35 @@ class AqlItemBlock {
349345
/// be used to recreate the AqlItemBlock via the Json constructor
350346
void toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Builder&) const;
351347

348+
/// @brief test if the given row is a shadow row and conveys subquery
349+
/// information only. It should not be handed to any non-subquery executor.
350+
bool isShadowRow(size_t row) const {
351+
/// This value is only filled for shadowRows.
352+
/// And it is guaranteed to be only filled by numbers this way.
353+
return _data[getSubqueryDepthAddress(row)].isNumber();
354+
}
355+
356+
AqlValue const& getShadowRowDepth(size_t row) const {
357+
TRI_ASSERT(isShadowRow(row));
358+
return _data[getSubqueryDepthAddress(row)];
359+
}
360+
361+
void setShadowRowDepth(size_t row, AqlValue const& other) {
362+
TRI_ASSERT(other.isNumber());
363+
_data[getSubqueryDepthAddress(row)] = other;
364+
TRI_ASSERT(isShadowRow(row));
365+
}
366+
367+
void makeShadowRow(size_t row) {
368+
TRI_ASSERT(!isShadowRow(row));
369+
_data[getSubqueryDepthAddress(row)] = AqlValue{VPackSlice::zeroSlice()};
370+
}
371+
372+
void makeDataRow(size_t row) {
373+
TRI_ASSERT(isShadowRow(row));
374+
_data[getSubqueryDepthAddress(row)] = AqlValue{VPackSlice::noneSlice()};
375+
}
376+
352377
protected:
353378
AqlItemBlockManager& aqlItemBlockManager() noexcept { return _manager; }
354379
size_t getRefCount() const noexcept { return _refCount; }
@@ -358,6 +383,33 @@ class AqlItemBlock {
358383
--_refCount;
359384
}
360385

386+
private:
387+
// This includes the amount of internal registers that are not visible to the outside.
388+
inline RegisterCount internalNrRegs() const noexcept { return _nrRegs + 1; }
389+
390+
/// @brief get the computed address within the data vector
391+
inline size_t getAddress(size_t index, RegisterId varNr) const noexcept {
392+
TRI_ASSERT(index < _nrItems);
393+
TRI_ASSERT(varNr < _nrRegs);
394+
return index * internalNrRegs() + varNr + 1;
395+
}
396+
397+
inline size_t getSubqueryDepthAddress(size_t index) const noexcept {
398+
TRI_ASSERT(index < _nrItems);
399+
return index * internalNrRegs();
400+
}
401+
402+
inline void copySubqueryDepth(size_t currentRow, size_t fromRow) {
403+
auto currentAddress = getSubqueryDepthAddress(currentRow);
404+
auto fromAddress = getSubqueryDepthAddress(fromRow);
405+
if (!_data[fromAddress].isEmpty() && _data[currentAddress].isEmpty()) {
406+
_data[currentAddress] = _data[fromAddress];
407+
}
408+
}
409+
410+
void copySubQueryDepthToOtherBlock(SharedAqlItemBlockPtr& target,
411+
size_t sourceRow, size_t targetRow) const;
412+
361413
private:
362414
/// @brief _data, the actual data as a single vector of dimensions _nrItems
363415
/// times _nrRegs
@@ -375,7 +427,7 @@ class AqlItemBlock {
375427
size_t _nrItems = 0;
376428

377429
/// @brief _nrRegs, number of columns
378-
RegisterId _nrRegs = 0;
430+
RegisterCount _nrRegs = 0;
379431

380432
/// @brief manager for this item block
381433
AqlItemBlockManager& _manager;

arangod/Aql/AqlItemBlockManager.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ using namespace arangodb::aql;
3131
using VelocyPackHelper = arangodb::basics::VelocyPackHelper;
3232

3333
/// @brief create the manager
34-
AqlItemBlockManager::AqlItemBlockManager(ResourceMonitor* resourceMonitor)
35-
: _resourceMonitor(resourceMonitor) {
34+
AqlItemBlockManager::AqlItemBlockManager(ResourceMonitor* resourceMonitor,
35+
SerializationFormat format)
36+
: _resourceMonitor(resourceMonitor), _format(format) {
3637
TRI_ASSERT(resourceMonitor != nullptr);
3738
}
3839

@@ -43,7 +44,7 @@ AqlItemBlockManager::~AqlItemBlockManager() = default;
4344
SharedAqlItemBlockPtr AqlItemBlockManager::requestBlock(size_t nrItems, RegisterId nrRegs) {
4445
// LOG_TOPIC("47298", TRACE, arangodb::Logger::FIXME) << "requesting AqlItemBlock of "
4546
// << nrItems << " x " << nrRegs;
46-
size_t const targetSize = nrItems * nrRegs;
47+
size_t const targetSize = nrItems * (nrRegs + 1);
4748

4849
AqlItemBlock* block = nullptr;
4950
size_t i = Bucket::getId(targetSize);
@@ -114,8 +115,10 @@ void AqlItemBlockManager::returnBlock(AqlItemBlock*& block) noexcept {
114115
}
115116

116117
SharedAqlItemBlockPtr AqlItemBlockManager::requestAndInitBlock(arangodb::velocypack::Slice slice) {
117-
auto const nrItemsSigned = VelocyPackHelper::getNumericValue<int64_t>(slice, "nrItems", 0);
118-
auto const nrRegs = VelocyPackHelper::getNumericValue<RegisterId>(slice, "nrRegs", 0);
118+
auto const nrItemsSigned =
119+
VelocyPackHelper::getNumericValue<int64_t>(slice, "nrItems", 0);
120+
auto const nrRegs =
121+
VelocyPackHelper::getNumericValue<RegisterId>(slice, "nrRegs", 0);
119122
if (nrItemsSigned <= 0) {
120123
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "nrItems must be > 0");
121124
}

arangod/Aql/AqlItemBlockManager.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#ifndef ARANGOD_AQL_AQL_ITEM_BLOCK_MANAGER_H
2525
#define ARANGOD_AQL_AQL_ITEM_BLOCK_MANAGER_H 1
2626

27+
#include "Aql/AqlItemBlockSerializationFormat.h"
2728
#include "Aql/SharedAqlItemBlockPtr.h"
2829
#include "Aql/types.h"
2930
#include "Basics/Common.h"
@@ -38,9 +39,10 @@ struct ResourceMonitor;
3839

3940
class AqlItemBlockManager {
4041
friend class SharedAqlItemBlockPtr;
42+
4143
public:
4244
/// @brief create the manager
43-
explicit AqlItemBlockManager(ResourceMonitor*);
45+
explicit AqlItemBlockManager(ResourceMonitor*, SerializationFormat format);
4446

4547
/// @brief destroy the manager
4648
TEST_VIRTUAL ~AqlItemBlockManager();
@@ -52,20 +54,22 @@ class AqlItemBlockManager {
5254
/// @brief request a block and initialize it from the slice
5355
TEST_VIRTUAL SharedAqlItemBlockPtr requestAndInitBlock(velocypack::Slice slice);
5456

55-
TEST_VIRTUAL ResourceMonitor* resourceMonitor() const noexcept { return _resourceMonitor; }
57+
TEST_VIRTUAL ResourceMonitor* resourceMonitor() const noexcept {
58+
return _resourceMonitor;
59+
}
60+
61+
SerializationFormat getFormatType() const { return _format; }
5662

5763
#ifdef ARANGODB_USE_GOOGLE_TESTS
5864
// Only used for the mocks in the catch tests. Other code should always use
5965
// SharedAqlItemBlockPtr which in turn call returnBlock()!
60-
static void deleteBlock(AqlItemBlock* block) {
61-
delete block;
62-
}
66+
static void deleteBlock(AqlItemBlock* block) { delete block; }
6367
#endif
6468

6569
#ifndef ARANGODB_USE_GOOGLE_TESTS
6670
protected:
6771
#else
68-
// make returnBlock public for tests so it can be mocked
72+
// make returnBlock public for tests so it can be mocked
6973
public:
7074
#endif
7175
/// @brief return a block to the manager
@@ -74,6 +78,7 @@ class AqlItemBlockManager {
7478

7579
private:
7680
ResourceMonitor* _resourceMonitor;
81+
SerializationFormat const _format;
7782

7883
static constexpr size_t numBuckets = 12;
7984
static constexpr size_t numBlocksPerBucket = 7;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
/// DISCLAIMER
3+
///
4+
/// Copyright 2019-2019 ArangoDB GmbH, Cologne, Germany
5+
///
6+
/// Licensed under the Apache License, Version 2.0 (the "License");
7+
/// you may not use this file except in compliance with the License.
8+
/// You may obtain a copy of the License at
9+
///
10+
/// http://www.apache.org/licenses/LICENSE-2.0
11+
///
12+
/// Unless required by applicable law or agreed to in writing, software
13+
/// distributed under the License is distributed on an "AS IS" BASIS,
14+
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
/// See the License for the specific language governing permissions and
16+
/// limitations under the License.
17+
///
18+
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
///
20+
/// @author Michael Hackstein
21+
////////////////////////////////////////////////////////////////////////////////
22+
23+
#ifndef ARANGOD_AQL_AQLITEMBLOCK_SERIALIZATION_FORMAT_H
24+
#define ARANGOD_AQL_AQLITEMBLOCK_SERIALIZATION_FORMAT_H
25+
26+
namespace arangodb {
27+
namespace aql {
28+
29+
/// @brief Defines the serialization format of AQL item blocks
30+
enum class SerializationFormat {
31+
// Format used in 3.5 and early. Not containing shadow rows
32+
CLASSIC = 0,
33+
// Use a hidden register for shadow rows. In classic versions all entries would be off by one.
34+
SHADOWROWS = 1
35+
};
36+
37+
} // namespace aql
38+
} // namespace arangodb
39+
#endif

arangod/Aql/AqlItemMatrix.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "Aql/AqlItemBlock.h"
2727
#include "Aql/InputAqlItemRow.h"
28+
#include "Aql/ShadowAqlItemRow.h"
2829
#include "Aql/types.h"
2930
#include "Basics/Common.h"
3031
#include "Basics/system-compiler.h"

0 commit comments

Comments
 (0)
0