8000 issue #150, issue #188 · arangodb/arangodb@13adf6d · GitHub
[go: up one dir, main page]

Skip to content

Commit 13adf6d

Browse files
committed
issue #150, issue #188
1 parent fc1fed6 commit 13adf6d

12 files changed

+987
-266
lines changed

arangod/V8Server/ApplicationV8.cpp

Lines changed: 146 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
////////////////////////////////////////////////////////////////////////////////
2-
/// @brief V8 enigne configuration
2+
/// @brief V8 engine configuration
33
///
44
/// @file
55
///
@@ -28,6 +28,8 @@
2828
#include "ApplicationV8.h"
2929

3030
#include "Basics/ConditionLocker.h"
31+
#include "Basics/ReadLocker.h"
32+
#include "Basics/WriteLocker.h"
3133
#include "Logger/Logger.h"
3234
#include "V8/v8-conv.h"
3335
#include "V8/v8-shell.h"
@@ -65,16 +67,43 @@ namespace {
6567
public:
6668
V8GcThread (ApplicationV8* applicationV8)
6769
: Thread("v8-gc"),
68-
_applicationV8(applicationV8) {
70+
_applicationV8(applicationV8),
71+
_lock(),
72+
_lastGcStamp(TRI_microtime()) {
6973
}
7074

7175
public:
76+
77+
////////////////////////////////////////////////////////////////////////////////
78+
/// @brief collect garbage in an endless loop (main functon of GC thread)
79+
////////////////////////////////////////////////////////////////////////////////
80+
7281
void run () {
7382
_applicationV8->collectGarbage();
7483
}
7584

85+
////////////////////////////////////////////////////////////////////////////////
86+
/// @brief get the timestamp of the last GC
87+
////////////////////////////////////////////////////////////////////////////////
88+
89+
double getLastGcStamp () {
90+
READ_LOCKER(_lock);
91+
return _lastGcStamp;
92+
}
93+
94+
////////////////////////////////////////////////////////////////////////////////
95+
/// @brief set the global GC timestamp
96+
////////////////////////////////////////////////////////////////////////////////
97+
98+
void updateGcStamp (double value) {
99+
WRITE_LOCKER(_lock);
100+
_lastGcStamp = value;
101+
}
102+
76103
private:
77104
ApplicationV8* _applicationV8;
105+
ReadWriteLock _lock;
106+
double _lastGcStamp;
78107
};
79108

80109
}
@@ -106,6 +135,7 @@ ApplicationV8::ApplicationV8 (string const& binaryPath)
106135
_startupModules("js/modules"),
107136
_actionPath(),
108137
_gcInterval(1000),
138+
_gcFrequency(10.0),
109139
_startupLoader(),
110140
_actionLoader(),
111141
_vocbase(0),
@@ -230,6 +260,10 @@ ApplicationV8::V8Context* ApplicationV8::enterContext () {
230260
////////////////////////////////////////////////////////////////////////////////
231261

2 A3E2 32262
void ApplicationV8::exitContext (V8Context* context) {
263+
V8GcThread* gc = dynamic_cast<V8GcThread*>(_gcThread);
264+
assert(gc != 0);
265+
double lastGc = gc->getLastGcStamp();
266+
233267
context->_context->Exit();
234268
context->_isolate->Exit();
235269
delete context->_locker;
@@ -239,39 +273,131 @@ void ApplicationV8::exitContext (V8Context* context) {
239273
{
240274
CONDITION_LOCKER(guard, _contextCondition);
241275

242-
if (context->_dirt < _gcInterval) {
243-
_freeContexts.push_back(context);
276+
if (context->_lastGcStamp + _gcFrequency < lastGc) {
277+
LOGGER_TRACE << "periodic gc interval reached";
278+
_dirtyContexts.push_back(context);
244279
}
245-
else {
280+
else if (context->_dirt >= _gcInterval) {
281+
LOGGER_TRACE << "maximum number of requests reached";
246282
_dirtyContexts.push_back(context);
247283
}
284+
else {
285+
_freeContexts.push_back(context);
286+
}
248287

249288
guard.broadcast();
250289
}
251290

252291
LOGGER_TRACE << "returned dirty V8 context";
253292
}
254293

294+
////////////////////////////////////////////////////////////////////////////////
295+
/// @brief determine which of the free contexts should be picked for the GC
296+
////////////////////////////////////////////////////////////////////////////////
297+
298+
ApplicationV8::V8Context* ApplicationV8::pickContextForGc () {
299+
size_t n = _freeContexts.size();
300+
301+
if (n == 0) {
302+
// this is easy...
303+
return 0;
304+
}
305+
306+
V8GcThread* gc = dynamic_cast<V8GcThread*>(_gcThread);
307+
V8Context* context = 0;
308+
309+
// we got more than 1 context to clean up, pick the one with the "oldest" GC stamp
310+
size_t pickedContextNr = 0; // index of context with lowest GC stamp
311+
312+
for (size_t i = 0; i < n; ++i) {
313+
// compare last GC stamp
314+
if (_freeContexts[i]->_lastGcStamp <= _freeContexts[pickedContextNr]->_lastGcStamp) {
315+
pickedContextNr = i;
316+
}
317+
}
318+
// we now have the context to clean up in pickedContextNr
319+
320+
// this is the context to clean up
321+
context = _freeContexts[pickedContextNr];
322+
assert(context != 0);
323+
324+
// now compare its last GC timestamp with the last global GC stamp
325+
if (context->_lastGcStamp + _gcFrequency >= gc->getLastGcStamp()) {
326+
// no need yet to clean up the context
327+
return 0;
328+
}
329+
330+
// we'll pop the context from the vector. the context might be at any position in the vector
331+
// so we need to move the other elements around
332+
if (n > 1) {
333+
for (size_t i = pickedContextNr; i < n - 1; ++i) {
334+
_freeContexts[i] = _freeContexts[i + 1];
335+
}
336+
}
337+
_freeContexts.pop_back();
338+
339+
return context;
340+
}
341+
255342
////////////////////////////////////////////////////////////////////////////////
256343
/// @brief runs the garbage collection
257344
////////////////////////////////////////////////////////////////////////////////
258345

259346
void ApplicationV8::collectGarbage () {
347+
V8GcThread* gc = dynamic_cast<V8GcThread*>(_gcThread);
348+
assert(gc != 0);
349+
350+
// this flag will be set to true if we timed out waiting for a GC signal
351+
// if set to true, the next cycle will use a reduced wait time so the GC
352+
// can be performed more early for all dirty contexts. The flag is set
353+
// to false again once all contexts have been cleaned up and there is nothing
354+
// more to do
355+
bool useReducedWait = false;
356+
357+
// the time we'll wait for a signal
358+
uint64_t regularWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 1000.0);
359+
360+
// the time we'll wait for a signal when the previous wait timed out
361+
uint64_t reducedWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 100.0);
362+
260363
while (_stopping == 0) {
261364
V8Context* context = 0;
365+
bool gotSignal = false;
262366

263367
{
264368
CONDITION_LOCKER(guard, _contextCondition);
265369

266370
if (_dirtyContexts.empty()) {
267-
guard.wait();
371+
uint64_t waitTime = useReducedWait ? reducedWaitTime : regularWaitTime;
372+
// we'll wait for a signal or a timeout
373+
gotSignal = guard.wait(waitTime);
374+
375+
// use a reduced wait time in the next round because we seem to be idle
376+
// the reduced wait time will allow use to perfom GC for more contexts
377+
useReducedWait = ! gotSignal;
268378
}
269379

270380
if (! _dirtyContexts.empty()) {
271381
context = _dirtyContexts.back();
272382
_dirtyContexts.pop_back();
383+
useReducedWait = false;
384+
}
385+
else if (! gotSignal && ! _freeContexts.empty()) {
386+
// we timed out waiting for a signal, so we have idle time that we can
387+
// spend on running the GC pro-actively
388+
// We'll pick one of the free contexts and clean it up
389+
context = pickContextForGc();
390+
391+
// there is no context to clean up, probably they all have been cleaned up
392+
// already. increase the wait time so we don't cycle to much in the GC loop
393+
// and waste CPU unnecessary
394+
useReducedWait = (context != 0);
273395
}
274396
}
397+
398+
// update last gc time
399+
double lastGc = TRI_microtime();
400+
gc->updateGcStamp(lastGc);
275401

276402
if (context != 0) {
277403
LOGGER_TRACE << "collecting V8 garbage";
@@ -288,6 +414,7 @@ void ApplicationV8::collectGarbage () {
288414
delete context->_locker;
289415

290416
context->_dirt = 0;
417+
context->_lastGcStamp = lastGc;
291418

292419
{
293420
CONDITION_LOCKER(guard, _contextCondition);
@@ -326,7 +453,8 @@ void ApplicationV8::disableActions () {
326453

327454
void ApplicationV8::setupOptions (map<string, basics::ProgramOptionsDescription>& options) {
328455
options["JAVASCRIPT Options:help-admin"]
329-
("javascript.gc-interval", &_gcInterval, "JavaScript garbage collection interval (each x requests)")
456+
("javascript.gc-interval", &_gcInterval, "JavaScript request-based garbage collection interval (each x requests)")
457+
("javascript.gc-frequency", &_gcFrequency, "JavaScript time-based garbage collection frequency (each x seconds)")
330458
;
331459

332460
options["JAVASCRIPT Options:help-admin"]
@@ -341,8 +469,14 @@ void ApplicationV8::setupOptions (map<string, basics::ProgramOptionsDescription>
341469
////////////////////////////////////////////////////////////////////////////////
342470

343471
bool ApplicationV8::prepare () {
472+
LOGGER_DEBUG << "V8 version: " << v8::V8::GetVersion();
344473
LOGGER_INFO << "using JavaScript modules path '" << _startupModules << "'";
345474

475+
if (_gcFrequency < 1) {
476+
// use a minimum of 1 second for GC
477+
_gcFrequency = 1;
478+
}
479+
346480
// set up the startup loader
347481
if (_startupPath.empty()) {
348482
LOGGER_INFO << "using built-in JavaScript startup files";
@@ -425,13 +559,15 @@ void ApplicationV8::shutdown () {
425559
usleep(1000);
426560
_gcThread->stop();
427561
_gcThread->join();
428-
delete _gcThread;
429562

430563
for (size_t i = 0; i < _nrInstances; ++i) {
431564
shutdownV8Instance(i);
432565
}
433566

434567
delete[] _contexts;
568+
569+
// delete GC thread after all action threads have been stopped
570+
delete _gcThread;
435571
}
436572

437573
////////////////////////////////////////////////////////////////////////////////
@@ -528,6 +664,8 @@ bool ApplicationV8::prepareV8Instance (size_t i) {
528664
context->_isolate->Exit();
529665
delete context->_locker;
530666

667+
context->_lastGcStamp = TRI_microtime();
668+
531669
LOGGER_TRACE << "initialised V8 context #" << i;
532670

533671
_freeContexts.push_back(context);

arangod/V8Server/ApplicationV8.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,19 @@ namespace triagens {
9191
v8::Persistent<v8::Context> _context;
9292
v8::Isolate* _isolate;
9393
v8::Locker* _locker;
94-
size_t _dirt;
94+
95+
////////////////////////////////////////////////////////////////////////////////
96+
/// @brief number of requests since last GC of the context
97+
////////////////////////////////////////////////////////////////////////////////
98+
99+
size_t _dirt;
100+
101+
////////////////////////////////////////////////////////////////////////////////
102+
/// @brief timestamp of last GC for the context
103+
////////////////////////////////////////////////////////////////////////////////
104+
105+
double _lastGcStamp;
106+
95107
};
96108

97109
////////////////////////////////////////////////////////////////////////////////
@@ -239,6 +251,14 @@ namespace triagens {
239251
/// @addtogroup ArangoDB
240252
/// @{
241253
////////////////////////////////////////////////////////////////////////////////
254+
255+
////////////////////////////////////////////////////////////////////////////////
256+
/// @brief determine which of the free contexts should be picked for the GC
257+
////////////////////////////////////////////////////////////////////////////////
258+
259+
V8Context* pickContextForGc ();
260+
261+
////////////////////////////////////////////////////////////////////////////////
242262

243263
////////////////////////////////////////////////////////////////////////////////
244264
/// @brief prepares a V8 instance
@@ -311,6 +331,18 @@ namespace triagens {
311331

312332
uint64_t _gcInterval;
313333

334+
////////////////////////////////////////////////////////////////////////////////
335+
/// @brief JavaScript garbage collection frequency (each x seconds)
336+
///
337+
/// @CMDOPT{--javascript.gc-frequency @CA{frequency}}
338+
///
339+
/// Specifies the frequency in seconds for the automatic garbage collection of
340+
/// JavaScript objects. This setting is useful to have the garbage collection
341+
/// still work in periods with no or little numbers of requests.
342+
////////////////////////////////////////////////////////////////////////////////
343+
344+
double _gcFrequency;
345+
314346
////////////////////////////////////////////////////////////////////////////////
315347
/// @brief V8 startup loader
316348
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)
0