From 712358d0694f4575c1de80c6e42a4b877fbb4a18 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Mon, 21 Jul 2014 15:48:43 +0200 Subject: [PATCH] added optional `ttl` attribute to specify result cursor expiration for HTTP API method `POST /_api/cursor` The `ttl` attribute can be used to prevent cursor results from timing out too early. --- CHANGELOG | 9 +- UnitTests/HttpInterface/api-cursor-spec.rb | 114 ++++++++++++++++++++- arangod/V8Server/v8-vocbase.cpp | 68 +++++++----- arangod/VocBase/general-cursor.cpp | 74 +++++++------ arangod/VocBase/general-cursor.h | 5 +- js/actions/api-cursor.js | 17 ++- js/actions/api-simple.js | 14 +-- 7 files changed, 222 insertions(+), 79 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c006ff033a..d50fba49d8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,16 @@ v2.2.1 (XXXX-XX-XX) ------------------- -* issue #947 Foxx applicationContext missing some properties +* added optional `ttl` attribute to specify result cursor expiration for HTTP API method + `POST /_api/cursor` + + The `ttl` attribute can be used to prevent cursor results from timing out too early. + +* issue #947: Foxx applicationContext missing some properties * (reported by Christian Neubauer): - The problem was that in googles V8, signed and unsignet chars are not always declared cleanly. + The problem was that in Google's V8, signed and unsigned chars are not always declared cleanly. so we need to force v8 to compile with forced signed chars which is done by the Flag: -fsigned-char at least it is enough to follow the instructions of compiling arango on rasperry diff --git a/UnitTests/HttpInterface/api-cursor-spec.rb b/UnitTests/HttpInterface/api-cursor-spec.rb index 866a4092e9..b15358d5c9 100644 --- a/UnitTests/HttpInterface/api-cursor-spec.rb +++ b/UnitTests/HttpInterface/api-cursor-spec.rb @@ -288,7 +288,7 @@ describe ArangoDB do doc.parsed_response['id'].should be_nil end - it "deleting an invalid cursor" do + it "deleting an invalid cursor" do cmd = api cmd = api + "/999999" # we assume this cursor id is invalid doc = ArangoDB.log_delete("#{prefix}-delete", cmd) @@ -300,6 +300,118 @@ describe ArangoDB do doc.parsed_response['code'].should eq(404) doc.parsed_response['id'].should be_nil end + + it "creates a cursor that will expire" do + cmd = api + body = "{ \"query\" : \"FOR u IN #{@cn} LIMIT 5 RETURN u.n\", \"count\" : true, \"batchSize\" : 1, \"ttl\" : 4 }" + doc = ArangoDB.log_post("#{prefix}-create-ttl", cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(201) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['id'].should match(@reId) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 1 + id = doc.parsed_response['id'] + + cmd = api + "/#{id}" + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['id'].should match(@reId) + doc.parsed_response['id'].should eq(id) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 1 + + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(id) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 15 # this should delete the cursor on the server + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1600) + doc.parsed_response['code'].should eq(404) + end + + it "creates a cursor that will not expire" do + cmd = api + body = "{ \"query\" : \"FOR u IN #{@cn} LIMIT 5 RETURN u.n\", \"count\" : true, \"batchSize\" : 1, \"ttl\" : 60 }" + doc = ArangoDB.log_post("#{prefix}-create-ttl", cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(201) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['id'].should match(@reId) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 1 + id = doc.parsed_response['id'] + + cmd = api + "/#{id}" + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['id'].should match(@reId) + doc.parsed_response['id'].should eq(id) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 1 + + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(id) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + + sleep 5 # this should not delete the cursor on the server + doc = ArangoDB.log_put("#{prefix}-create-ttl", cmd) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(id) + doc.parsed_response['hasMore'].should eq(true) + doc.parsed_response['count'].should eq(5) + doc.parsed_response['result'].length.should eq(1) + end end ################################################################################ diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 532ddb6f66..d5dfb448df 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -3486,9 +3486,10 @@ static v8::Handle ExecuteQueryNativeAhuacatl (TRI_aql_context_t* cont static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vocbase, TRI_aql_context_t* const context, - const TRI_json_t* const parameters, - const bool doCount, - const uint32_t batchSize) { + TRI_json_t const* parameters, + bool doCount, + uint32_t batchSize, + double cursorTtl) { v8::HandleScope scope; v8::TryCatch tryCatch; @@ -3499,7 +3500,7 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { - TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); v8g->_canceled = true; return scope.Close(result); @@ -3536,35 +3537,35 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo // transform the result into JSON first TRI_json_t* json = TRI_ObjectToJson(docs); - if (json == 0) { + if (json == nullptr) { TRI_V8_EXCEPTION_MEMORY(scope); } TRI_general_cursor_result_t* cursorResult = TRI_CreateResultAql(json); - if (cursorResult == 0) { + if (cursorResult == nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); TRI_V8_EXCEPTION_MEMORY(scope); } // extra return values - TRI_json_t* extra = 0; + TRI_json_t* extra = nullptr; if (resultObject->Has(TRI_V8_SYMBOL("extra"))) { extra = TRI_ObjectToJson(resultObject->Get(TRI_V8_SYMBOL("extra"))); } - TRI_general_cursor_t* cursor = TRI_CreateGeneralCursor(vocbase, cursorResult, doCount, batchSize, extra); + TRI_general_cursor_t* cursor = TRI_CreateGeneralCursor(vocbase, cursorResult, doCount, batchSize, cursorTtl, extra); - if (cursor == 0) { + if (cursor == nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, cursorResult); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); - if (extra != 0) { + if (extra != nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, extra); } TRI_V8_EXCEPTION_MEMORY(scope); } - TRI_ASSERT(cursor != 0); + TRI_ASSERT(cursor != nullptr); v8::Handle cursorObject = WrapGeneralCursor(cursor); @@ -4317,12 +4318,12 @@ static v8::Handle JS_CreateCursor (v8::Arguments const& argv) { TRI_vocbase_t* vocbase = GetContextVocBase(); - if (vocbase == 0) { + if (vocbase == nullptr) { TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (argv.Length() < 1) { - TRI_V8_EXCEPTION_USAGE(scope, "CREATE_CURSOR(, , )"); + TRI_V8_EXCEPTION_USAGE(scope, "CREATE_CURSOR(, , , )"); } if (! argv[0]->IsArray()) { @@ -4333,7 +4334,7 @@ static v8::Handle JS_CreateCursor (v8::Arguments const& argv) { v8::Handle array = v8::Handle::Cast(argv[0]); TRI_json_t* json = TRI_ObjectToJson(array); - if (json == 0) { + if (json == nullptr) { TRI_V8_TYPE_ERROR(scope, "cannot convert to JSON"); } @@ -4355,14 +4356,23 @@ static v8::Handle JS_CreateCursor (v8::Arguments const& argv) { } } + double ttl = 0.0; + if (argv.Length() >= 4) { + ttl = TRI_ObjectToDouble(argv[3]); + } + + if (ttl <= 0.0) { + ttl = 30.0; // default ttl + } + // create a cursor - TRI_general_cursor_t* cursor = 0; + TRI_general_cursor_t* cursor = nullptr; TRI_general_cursor_result_t* cursorResult = TRI_CreateResultAql(json); - if (cursorResult != 0) { - cursor = TRI_CreateGeneralCursor(vocbase, cursorResult, doCount, batchSize, 0); + if (cursorResult != nullptr) { + cursor = TRI_CreateGeneralCursor(vocbase, cursorResult, doCount, batchSize, ttl, 0); - if (cursor == 0) { + if (cursor == nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, cursorResult); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); } @@ -4371,7 +4381,7 @@ static v8::Handle JS_CreateCursor (v8::Arguments const& argv) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); } - if (cursor == 0) { + if (cursor == nullptr) { TRI_V8_EXCEPTION_INTERNAL(scope, "cannot create cursor"); } @@ -4517,7 +4527,7 @@ static v8::Handle JS_PersistGeneralCursor (v8::Arguments const& argv) TRI_V8_EXCEPTION_USAGE(scope, "persist()"); } - TRI_PersistGeneralCursor(UnwrapGeneralCursor(argv.Holder()), 30.0); + TRI_PersistGeneralCursor(UnwrapGeneralCursor(argv.Holder())); return scope.Close(v8::True()); } @@ -5370,6 +5380,9 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { // maximum number of results to return at once uint32_t batchSize = UINT32_MAX; + + // ttl for cursor + double ttl = 0.0; if (argc > 2 && argv[2]->IsObject()) { // treat the argument as an object from now on @@ -5386,12 +5399,21 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { batchSize = (uint32_t) maxValue; } } + + if (options->Has(TRI_V8_SYMBOL("ttl"))) { + ttl = TRI_ObjectToDouble(options->Get(TRI_V8_SYMBOL("ttl"))); + } + } + + if (ttl <= 0.0) { + // default ttl + ttl = 30.0; } // user options // ------------------------------------------------- - TRI_json_t* userOptions = 0; + TRI_json_t* userOptions = nullptr; if (argc > 3 && argv[3]->IsObject()) { // treat the argument as an object from now on v8::Handle options = v8::Handle::Cast(argv[3]); @@ -5413,7 +5435,7 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { TRI_V8_EXCEPTION_MEMORY(scope); } - v8::Handle result = ExecuteQueryCursorAhuacatl(vocbase, context.ptr(), parameters, doCount, batchSize); + v8::Handle result = ExecuteQueryCursorAhuacatl(vocbase, context.ptr(), parameters, doCount, batchSize, ttl); int res = context.ptr()->_error._code; if (res == TRI_ERROR_REQUEST_CANCELED) { @@ -5446,7 +5468,7 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(errorObject)); } else { - TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); v8g->_canceled = true; return scope.Close(result); diff --git a/arangod/VocBase/general-cursor.cpp b/arangod/VocBase/general-cursor.cpp index 7341d70fa5..caef89ffdf 100644 --- a/arangod/VocBase/general-cursor.cpp +++ b/arangod/VocBase/general-cursor.cpp @@ -64,16 +64,15 @@ TRI_general_cursor_result_t* TRI_CreateCursorResult (void* data, TRI_general_cursor_row_t (*getAt)(TRI_general_cursor_result_t const*, const TRI_general_cursor_length_t), TRI_general_cursor_length_t (*getLength)(TRI_general_cursor_result_t const*)) { - TRI_general_cursor_result_t* result; - if (data == NULL) { - return NULL; + if (data == nullptr) { + return nullptr; } - result = (TRI_general_cursor_result_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_general_cursor_result_t), false); + TRI_general_cursor_result_t* result = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_general_cursor_result_t), false)); - if (result == NULL) { - return NULL; + if (result == nullptr) { + return nullptr; } result->_data = data; @@ -104,7 +103,7 @@ void TRI_DestroyCursorResult (TRI_general_cursor_result_t* const result) { //////////////////////////////////////////////////////////////////////////////// void TRI_FreeCursorResult (TRI_general_cursor_result_t* const result) { - if (result != NULL) { + if (result != nullptr) { TRI_DestroyCursorResult(result); TRI_Free(TRI_UNKNOWN_MEM_ZONE, result); } @@ -161,7 +160,7 @@ static inline TRI_general_cursor_row_t NextGeneralCursor (TRI_general_cursor_t* cursor->_result->freeData(cursor->_result); } - return NULL; + return nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -205,7 +204,7 @@ static TRI_json_t* GetExtraGeneralCursor (const TRI_general_cursor_t* const curs //////////////////////////////////////////////////////////////////////////////// void TRI_FreeGeneralCursor (TRI_general_cursor_t* cursor) { - if (cursor->_extra != NULL) { + if (cursor->_extra != nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, cursor->_extra); } @@ -225,15 +224,21 @@ TRI_general_cursor_t* TRI_CreateGeneralCursor (TRI_vocbase_t* vocbase, TRI_general_cursor_result_t* result, const bool doCount, const TRI_general_cursor_length_t batchSize, + double ttl, TRI_json_t* extra) { - TRI_general_cursor_t* cursor; + TRI_ASSERT(vocbase != nullptr); - TRI_ASSERT(vocbase != NULL); + TRI_general_cursor_t* cursor = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_general_cursor_t), false)); - cursor = (TRI_general_cursor_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_general_cursor_t), false); + if (cursor == nullptr) { + return nullptr; + } - if (cursor == NULL) { - return NULL; + if (ttl <= 0.0) { + ttl = 30.0; // default ttl + } + else if (ttl >= 3600.0) { + ttl = 3600.0; // max ttl } cursor->_vocbase = vocbase; @@ -242,9 +247,10 @@ TRI_general_cursor_t* TRI_CreateGeneralCursor (TRI_vocbase_t* vocbase, cursor->_result = result; cursor->_extra = extra; // might be NULL - cursor->_expires = TRI_microtime() + 3600; // default lifetime: 1h + cursor->_ttl = ttl; + cursor->_expires = TRI_microtime() + ttl; cursor->_id = TRI_NewTickServer(); - + // state cursor->_currentRow = 0; cursor->_length = result->getLength(result); @@ -299,12 +305,13 @@ TRI_general_cursor_t* TRI_UseGeneralCursor (TRI_general_cursor_t* cursor) { TRI_LockSpin(&store->_lock); cursor = static_cast(TRI_LookupByKeyAssociativePointer(&store->_ids, &cursor->_id)); - if (cursor != NULL) { + if (cursor != nullptr) { if (cursor->_usage._isDeleted) { - cursor = NULL; + cursor = nullptr; } else { ++cursor->_usage._refCount; + cursor->_expires = TRI_microtime() + cursor->_ttl; } } TRI_UnlockSpin(&store->_lock); @@ -321,7 +328,7 @@ void TRI_ReleaseGeneralCursor (TRI_general_cursor_t* cursor) { TRI_LockSpin(&store->_lock); cursor = static_cast(TRI_LookupByKeyAssociativePointer(&store->_ids, &cursor->_id)); - if (cursor != NULL) { + if (cursor != nullptr) { --cursor->_usage._refCount; } TRI_UnlockSpin(&store->_lock); @@ -338,7 +345,7 @@ bool TRI_DropGeneralCursor (TRI_general_cursor_t* cursor) { TRI_LockSpin(&store->_lock); cursor = static_cast(TRI_LookupByKeyAssociativePointer(&store->_ids, &cursor->_id)); - if (cursor != NULL && ! cursor->_usage._isDeleted) { + if (cursor != nullptr && ! cursor->_usage._isDeleted) { cursor->_usage._isDeleted = true; result = true; } @@ -370,9 +377,8 @@ size_t TRI_CountGeneralCursor (TRI_general_cursor_t* cursor) { /// @brief persist the cursor by setting a timeout //////////////////////////////////////////////////////////////////////////////// -void TRI_PersistGeneralCursor (TRI_general_cursor_t* cursor, - double ttl) { - cursor->_expires = TRI_microtime() + ttl; +void TRI_PersistGeneralCursor (TRI_general_cursor_t* cursor) { + cursor->_expires = TRI_microtime() + cursor->_ttl; } //////////////////////////////////////////////////////////////////////////////// @@ -385,8 +391,8 @@ TRI_general_cursor_t* TRI_FindGeneralCursor (TRI_vocbase_t* vocbase, TRI_LockSpin(&store->_lock); TRI_general_cursor_t* cursor = static_cast(TRI_LookupByKeyAssociativePointer(&store->_ids, &id)); - if (cursor == NULL || cursor->_usage._isDeleted) { - cursor = NULL; + if (cursor == nullptr || cursor->_usage._isDeleted) { + cursor = nullptr; } TRI_UnlockSpin(&store->_lock); @@ -404,7 +410,7 @@ bool TRI_RemoveGeneralCursor (TRI_vocbase_t* vocbase, TRI_LockSpin(&store->_lock); TRI_general_cursor_t* cursor = static_cast(TRI_LookupByKeyAssociativePointer(&store->_ids, &id)); - if (cursor == NULL || cursor->_usage._isDeleted) { + if (cursor == nullptr || cursor->_usage._isDeleted) { result = false; } else { @@ -423,8 +429,8 @@ bool TRI_RemoveGeneralCursor (TRI_vocbase_t* vocbase, TRI_general_cursor_store_t* TRI_CreateStoreGeneralCursor (void) { TRI_general_cursor_store_t* store = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_general_cursor_t), false)); - if (store == NULL) { - return NULL; + if (store == nullptr) { + return nullptr; } int res = TRI_InitAssociativePointer(&store->_ids, @@ -432,12 +438,12 @@ TRI_general_cursor_store_t* TRI_CreateStoreGeneralCursor (void) { HashKeyId, HashElementId, EqualKeyId, - NULL); + nullptr); if (res != TRI_ERROR_NO_ERROR) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, store); - return NULL; + return nullptr; } TRI_InitSpin(&store->_lock); @@ -485,13 +491,12 @@ void TRI_CleanupGeneralCursor (TRI_general_cursor_store_t* store, // we have deleted CURSOR_MAX_DELETE elements while (deleteCount++ < CURSOR_MAX_DELETE || force) { bool deleted = false; - size_t i; - for (i = 0; i < store->_ids._nrAlloc; i++) { + for (size_t i = 0; i < store->_ids._nrAlloc; i++) { // enum all cursors - TRI_general_cursor_t* cursor = (TRI_general_cursor_t*) store->_ids._table[i]; + TRI_general_cursor_t* cursor = static_cast(store->_ids._table[i]); - if (cursor == NULL) { + if (cursor == nullptr) { continue; } @@ -500,6 +505,7 @@ void TRI_CleanupGeneralCursor (TRI_general_cursor_store_t* store, if (force || (cursor->_usage._refCount == 0 && (cursor->_usage._isDeleted || cursor->_expires < compareStamp))) { + LOG_TRACE("cleaning cursor %p, id: %llu, rc: %d, expires: %d, deleted: %d", cursor, (unsigned long long) cursor->_id, diff --git a/arangod/VocBase/general-cursor.h b/arangod/VocBase/general-cursor.h index 5fcd542646..b42863bf5f 100644 --- a/arangod/VocBase/general-cursor.h +++ b/arangod/VocBase/general-cursor.h @@ -145,6 +145,7 @@ typedef struct TRI_general_cursor_s { uint32_t _refCount; bool _isDeleted; } _usage; + double _ttl; double _expires; struct TRI_json_s* _extra; @@ -177,6 +178,7 @@ TRI_general_cursor_t* TRI_CreateGeneralCursor (struct TRI_vocbase_s*, TRI_general_cursor_result_t*, const bool, const TRI_general_cursor_length_t, + double, struct TRI_json_s*); //////////////////////////////////////////////////////////////////////////////// @@ -225,8 +227,7 @@ size_t TRI_CountGeneralCursor (TRI_general_cursor_t*); /// @brief persist the cursor by setting a timeout //////////////////////////////////////////////////////////////////////////////// -void TRI_PersistGeneralCursor (TRI_general_cursor_t*, - double); +void TRI_PersistGeneralCursor (TRI_general_cursor_t*); //////////////////////////////////////////////////////////////////////////////// /// @brief lookup a cursor by its id diff --git a/js/actions/api-cursor.js b/js/actions/api-cursor.js index c430a8c232..24c780af8f 100644 --- a/js/actions/api-cursor.js +++ b/js/actions/api-cursor.js @@ -41,11 +41,6 @@ var internal = require("internal"); // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_post_api_cursor /// @brief create a cursor and return the first results @@ -74,6 +69,11 @@ var internal = require("internal"); /// the server to the client in one roundtrip (optional). If this attribute is /// not set, a server-controlled default value will be used. /// +/// - *ttl*: an optional time-to-live for the cursor (in seconds). The cursor will be +/// removed on the server automatically after the specified amount of time. This +/// is useful to ensure garbage collection of cursors that are not fully fetched +/// by clients. If not set, a server-defined value will be used. +/// /// - *bindVars*: key/value list of bind parameters (optional). /// /// - *options*: key/value list of extra options for the query (optional). @@ -349,7 +349,8 @@ function post_api_cursor(req, res) { json.bindVars, { count : json.count || false, - batchSize: json.batchSize || 1000 + batchSize: json.batchSize || 1000, + ttl: json.ttl }, json.options); } @@ -613,10 +614,6 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" diff --git a/js/actions/api-simple.js b/js/actions/api-simple.js index 1ea92aca48..20b44eabd8 100644 --- a/js/actions/api-simple.js +++ b/js/actions/api-simple.js @@ -345,7 +345,7 @@ function setupIndexQuery (name, func, isExampleQuery) { result.limit(limit); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } catch (err) { @@ -503,7 +503,7 @@ actions.defineHttp({ result = result.limit(limit); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } } @@ -779,7 +779,7 @@ actions.defineHttp({ result = result.distance(distance); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } } @@ -956,7 +956,7 @@ actions.defineHttp({ result = result.distance(distance); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } } @@ -1080,7 +1080,7 @@ actions.defineHttp({ result = result.limit(limit); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } } @@ -1235,7 +1235,7 @@ actions.defineHttp({ result = result.limit(limit); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } } @@ -1730,7 +1730,7 @@ actions.defineHttp({ result = result.limit(limit); } - createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize)); + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } }