mirror of https://gitee.com/bigwinds/arangodb
fix memory hunger of cache (#4269)
This commit is contained in:
parent
bd8f474528
commit
7b370dbf23
|
@ -1,7 +1,14 @@
|
||||||
|
v3.3.3 (XXXX-XX-XX)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* fixed issue #4255: AQL SORT consuming too much memory
|
||||||
|
|
||||||
|
|
||||||
v3.3.2 (2018-01-04)
|
v3.3.2 (2018-01-04)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
* fixed issue #4199: Internal failure: JavaScript exception in file 'arangosh.js' at 98,7: ArangoError 4: Expecting type String
|
* fixed issue #4199: Internal failure: JavaScript exception in file 'arangosh.js'
|
||||||
|
at 98,7: ArangoError 4: Expecting type String
|
||||||
|
|
||||||
* fixed issue in agency supervision with a good server being left in
|
* fixed issue in agency supervision with a good server being left in
|
||||||
failedServers
|
failedServers
|
||||||
|
|
|
@ -306,7 +306,8 @@ AqlItemBlock* GatherBlock::getSome(size_t atLeast, size_t atMost) {
|
||||||
size_t toSend = (std::min)(available, atMost); // nr rows in outgoing block
|
size_t toSend = (std::min)(available, atMost); // nr rows in outgoing block
|
||||||
|
|
||||||
// the following is similar to AqlItemBlock's slice method . . .
|
// the following is similar to AqlItemBlock's slice method . . .
|
||||||
std::unordered_set<AqlValue> cache;
|
std::vector<std::unordered_map<AqlValue, AqlValue>> cache;
|
||||||
|
cache.resize(_gatherBlockBuffer.size());
|
||||||
|
|
||||||
// comparison function
|
// comparison function
|
||||||
OurLessThan ourLessThan(_trx, _gatherBlockBuffer, _sortRegisters);
|
OurLessThan ourLessThan(_trx, _gatherBlockBuffer, _sortRegisters);
|
||||||
|
@ -341,9 +342,9 @@ AqlItemBlock* GatherBlock::getSome(size_t atLeast, size_t atMost) {
|
||||||
TRI_ASSERT(!_gatherBlockBuffer[val.first].empty());
|
TRI_ASSERT(!_gatherBlockBuffer[val.first].empty());
|
||||||
AqlValue const& x(_gatherBlockBuffer[val.first].front()->getValueReference(val.second, col));
|
AqlValue const& x(_gatherBlockBuffer[val.first].front()->getValueReference(val.second, col));
|
||||||
if (!x.isEmpty()) {
|
if (!x.isEmpty()) {
|
||||||
auto it = cache.find(x);
|
auto it = cache[val.first].find(x);
|
||||||
|
|
||||||
if (it == cache.end()) {
|
if (it == cache[val.first].end()) {
|
||||||
AqlValue y = x.clone();
|
AqlValue y = x.clone();
|
||||||
try {
|
try {
|
||||||
res->setValue(i, col, y);
|
res->setValue(i, col, y);
|
||||||
|
@ -351,9 +352,9 @@ AqlItemBlock* GatherBlock::getSome(size_t atLeast, size_t atMost) {
|
||||||
y.destroy();
|
y.destroy();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
cache.emplace(y);
|
cache[val.first].emplace(x, y);
|
||||||
} else {
|
} else {
|
||||||
res->setValue(i, col, (*it));
|
res->setValue(i, col, (*it).second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -382,6 +383,7 @@ AqlItemBlock* GatherBlock::getSome(size_t atLeast, size_t atMost) {
|
||||||
// more data for the shard for which we have no more local
|
// more data for the shard for which we have no more local
|
||||||
// values.
|
// values.
|
||||||
getBlock(val.first, atLeast, atMost);
|
getBlock(val.first, atLeast, atMost);
|
||||||
|
cache[val.first].clear();
|
||||||
// note that if getBlock() returns false here, this is not
|
// note that if getBlock() returns false here, this is not
|
||||||
// a problem, because the sort function used takes care of
|
// a problem, because the sort function used takes care of
|
||||||
// this
|
// this
|
||||||
|
|
|
@ -31,9 +31,11 @@ using namespace arangodb::aql;
|
||||||
|
|
||||||
SortBlock::SortBlock(ExecutionEngine* engine, SortNode const* en)
|
SortBlock::SortBlock(ExecutionEngine* engine, SortNode const* en)
|
||||||
: ExecutionBlock(engine, en), _sortRegisters(), _stable(en->_stable), _mustFetchAll(true) {
|
: ExecutionBlock(engine, en), _sortRegisters(), _stable(en->_stable), _mustFetchAll(true) {
|
||||||
|
|
||||||
|
auto regPlan = en->getRegisterPlan();
|
||||||
for (auto const& p : en->_elements) {
|
for (auto const& p : en->_elements) {
|
||||||
auto it = en->getRegisterPlan()->varInfo.find(p.var->id);
|
auto it = regPlan->varInfo.find(p.var->id);
|
||||||
TRI_ASSERT(it != en->getRegisterPlan()->varInfo.end());
|
TRI_ASSERT(it != regPlan->varInfo.end());
|
||||||
TRI_ASSERT(it->second.registerId < ExecutionNode::MaxRegisterId);
|
TRI_ASSERT(it->second.registerId < ExecutionNode::MaxRegisterId);
|
||||||
_sortRegisters.emplace_back(
|
_sortRegisters.emplace_back(
|
||||||
std::make_pair(it->second.registerId, p.ascending));
|
std::make_pair(it->second.registerId, p.ascending));
|
||||||
|
@ -126,7 +128,7 @@ void SortBlock::doSorting() {
|
||||||
count = 0;
|
count = 0;
|
||||||
RegisterId const nrRegs = _buffer.front()->getNrRegs();
|
RegisterId const nrRegs = _buffer.front()->getNrRegs();
|
||||||
|
|
||||||
std::unordered_set<AqlValue> cache;
|
std::unordered_map<AqlValue, AqlValue> cache;
|
||||||
|
|
||||||
// install the rearranged values from _buffer into newbuffer
|
// install the rearranged values from _buffer into newbuffer
|
||||||
|
|
||||||
|
@ -144,7 +146,6 @@ void SortBlock::doSorting() {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.clear();
|
|
||||||
// only copy as much as needed!
|
// only copy as much as needed!
|
||||||
for (size_t i = 0; i < sizeNext; i++) {
|
for (size_t i = 0; i < sizeNext; i++) {
|
||||||
for (RegisterId j = 0; j < nrRegs; j++) {
|
for (RegisterId j = 0; j < nrRegs; j++) {
|
||||||
|
@ -160,7 +161,7 @@ void SortBlock::doSorting() {
|
||||||
// the new block already has either a copy or stolen
|
// the new block already has either a copy or stolen
|
||||||
// the AqlValue:
|
// the AqlValue:
|
||||||
_buffer[coords[count].first]->eraseValue(coords[count].second, j);
|
_buffer[coords[count].first]->eraseValue(coords[count].second, j);
|
||||||
next->setValue(i, j, (*it));
|
next->setValue(i, j, (*it).second);
|
||||||
} else {
|
} else {
|
||||||
// We need to copy a, if it has already been stolen from
|
// We need to copy a, if it has already been stolen from
|
||||||
// its original buffer, which we know by looking at the
|
// its original buffer, which we know by looking at the
|
||||||
|
@ -174,7 +175,7 @@ void SortBlock::doSorting() {
|
||||||
TRI_IF_FAILURE("SortBlock::doSortingCache") {
|
TRI_IF_FAILURE("SortBlock::doSortingCache") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
cache.emplace(b);
|
cache.emplace(a, b);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
b.destroy();
|
b.destroy();
|
||||||
throw;
|
throw;
|
||||||
|
@ -186,15 +187,14 @@ void SortBlock::doSorting() {
|
||||||
}
|
}
|
||||||
next->setValue(i, j, b);
|
next->setValue(i, j, b);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
cache.erase(b);
|
cache.erase(a);
|
||||||
b.destroy();
|
b.destroy();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
// It does not matter whether the following works or not,
|
// It does not matter whether the following works or not,
|
||||||
// since the original block keeps its responsibility
|
// since the original block keeps its responsibility
|
||||||
// for a:
|
// for a:
|
||||||
_buffer[coords[count].first]->eraseValue(coords[count].second,
|
_buffer[coords[count].first]->eraseValue(coords[count].second, j);
|
||||||
j);
|
|
||||||
} else {
|
} else {
|
||||||
TRI_IF_FAILURE("SortBlock::doSortingNext2") {
|
TRI_IF_FAILURE("SortBlock::doSortingNext2") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
@ -203,13 +203,12 @@ void SortBlock::doSorting() {
|
||||||
// steal it:
|
// steal it:
|
||||||
next->setValue(i, j, a);
|
next->setValue(i, j, a);
|
||||||
_buffer[coords[count].first]->steal(a);
|
_buffer[coords[count].first]->steal(a);
|
||||||
_buffer[coords[count].first]->eraseValue(coords[count].second,
|
_buffer[coords[count].first]->eraseValue(coords[count].second, j);
|
||||||
j);
|
// This might throw as well, however, the responsibility
|
||||||
// If this has worked, responsibility is now with the
|
// is already with the new block.
|
||||||
// new block or indeed with us!
|
|
||||||
// If the following does not work, we will create a
|
// If the following does not work, we will create a
|
||||||
// few unnecessary copies, but this does not matter:
|
// few unnecessary copies, but this does not matter:
|
||||||
cache.emplace(a);
|
cache.emplace(a, a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,9 +259,9 @@ function ahuacatlFailureSuite () {
|
||||||
testSortBlock5 : function () {
|
testSortBlock5 : function () {
|
||||||
internal.debugSetFailAt("SortBlock::doSortingNext2");
|
internal.debugSetFailAt("SortBlock::doSortingNext2");
|
||||||
// we need values that are >= 16 bytes long
|
// we need values that are >= 16 bytes long
|
||||||
assertFailingQuery("FOR i IN " + c.name() + " COLLECT key = i._key SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN key");
|
assertFailingQuery("LET x = NOOPT('xxxxxxxxxxxxxxxxxxxx') FOR i IN " + c.name() + " COLLECT key = i._key SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN { key, x }");
|
||||||
assertFailingQuery("FOR i IN " + c.name() + " COLLECT key = i.value SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN key");
|
assertFailingQuery("LET x = NOOPT('xxxxxxxxxxxxxxxxxxxx') FOR i IN " + c.name() + " COLLECT key = i.value SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN { key, x }");
|
||||||
assertFailingQuery("FOR i IN " + c.name() + " COLLECT key = i.value2 SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN key");
|
assertFailingQuery("LET x = NOOPT('xxxxxxxxxxxxxxxxxxxx') FOR i IN " + c.name() + " COLLECT key = i.value2 SORT CONCAT('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', key) RETURN { key, x }");
|
||||||
},
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue