Timeout Notification for Asynchronous Request - timeout

I am sending SPARQL queries as asynchronous requests to a SPARQL endpoint, currently DBpedia using the dotNetRDF library. While simpler queries usually work, more complex queries sometimes result in timeouts.
I am looking for a way to handle the timeouts by capturing some event when they occur.
I am sending my queries by using one of the asynchronous QueryWithResultSet overloads of the SparqlRemoteEndpoint class.
As described for SparqlResultsCallback, the state object will be replaced with an AsyncError instance if the asynchronous request failed. This does indicate that there was a timeout, however it seems that it only does so 10 minutes after the request was sent. When my timeout is, for example, 30 seconds, I would like to know 30 seconds later whether the request was successful. (35 seconds are ok, too, but you get the idea.)
Here is a sample application that sends two requests, the first of which is very simple and likely to succeed within the timeout (here set to 120 seconds), while the second one is rather complex and may easily fail on DBpedia:
using System;
using System.Collections.Concurrent;
using VDS.RDF;
using VDS.RDF.Query;
public class TestTimeout
{
private static string FormatResults(SparqlResultSet results, object state)
{
var result = new System.Text.StringBuilder();
result.AppendLine(DateTime.Now.ToLongTimeString());
var asyncError = state as AsyncError;
if (asyncError != null) {
result.AppendLine(asyncError.State.ToString());
result.AppendLine(asyncError.Error.ToString());
} else {
result.AppendLine(state.ToString());
}
if (results == null) {
result.AppendLine("results == null");
} else {
result.AppendLine("results.Count == " + results.Count.ToString());
}
return result.ToString();
}
public static void Main(string[] args)
{
Console.WriteLine("Launched ...");
Console.WriteLine(DateTime.Now.ToLongTimeString());
var output = new BlockingCollection<string>();
var ep = new SparqlRemoteEndpoint(new Uri("http://dbpedia.org/sparql"));
ep.Timeout = 120;
Console.WriteLine("Server == " + ep.Uri.AbsoluteUri);
Console.WriteLine("HTTP Method == " + ep.HttpMode);
Console.WriteLine("Timeout == " + ep.Timeout.ToString());
string query = "SELECT DISTINCT ?a\n"
+ "WHERE {\n"
+ " ?a <http://www.w3.org/2000/01/rdf-schema#label> ?b.\n"
+ "}\n"
+ "LIMIT 10\n";
ep.QueryWithResultSet(query,
(results, state) => {
output.Add(FormatResults(results, state));
},
"Query 1");
query = "SELECT DISTINCT ?v5 ?v8\n"
+ "WHERE {\n"
+ " {\n"
+ " SELECT DISTINCT ?v5\n"
+ " WHERE {\n"
+ " ?v6 ?v5 ?v7.\n"
+ " FILTER(regex(str(?v5), \"[/#]c[^/#]*$\", \"i\")).\n"
+ " }\n"
+ " OFFSET 0\n"
+ " LIMIT 20\n"
+ " }.\n"
+ " OPTIONAL {\n"
+ " ?v5 <http://www.w3.org/2000/01/rdf-schema#label> ?v8.\n"
+ " FILTER(lang(?v8) = \"en\").\n"
+ " }.\n"
+ "}\n"
+ "ORDER BY str(?v5)\n";
ep.QueryWithResultSet(query,
(results, state) => {
output.Add(FormatResults(results, state));
},
"Query 2");
Console.WriteLine("Queries sent.");
Console.WriteLine(DateTime.Now.ToLongTimeString());
Console.WriteLine();
string result = output.Take();
Console.WriteLine(result);
result = output.Take();
Console.WriteLine(result);
Console.ReadLine();
}
}
When I run this, I reproducibly get an output like the following:
13:13:23
Server == http://dbpedia.org/sparql
HTTP Method == GET
Timeout == 120
Queries sent.
13:13:25
13:13:25
Query 1
results.Count == 10
13:23:25
Query 2
VDS.RDF.Query.RdfQueryException: A HTTP error occurred while making an asynchron
ous query, see inner exception for details ---> System.Net.WebException: Der Rem
oteserver hat einen Fehler zurückgegeben: (504) Gatewaytimeout.
bei System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
bei VDS.RDF.Query.SparqlRemoteEndpoint.<>c__DisplayClass13.<QueryWithResultSe
t>b__11(IAsyncResult innerResult)
--- Ende der internen Ausnahmestapelüberwachung ---
results == null
Obviously, the exact times will be different, but the crucial point is that the error message based on the second query is received approximately 10 minutes after the request was sent, nowhere near the 2 minutes set for the timeout.
Am I using dotNetRDF incorrectly here, or is it intentional that I have to run an additional timer to measure the timeout myself and react on my own unless any response has been received meanwhile?

No you are not using dotNetRDF incorrectly rather there appears to be a bug that the timeouts set on an endpoint don't get honoured when running queries asynchronously. This has been filed as CORE-393
By the way even with this bug fixed you won't necessarily get a hard timeout at the set timeout. Essentially the value you set for the Timeout property of the SparqlRemoteEndpoint instance that value is used to set the Timeout property of the .Net HttpWebRequest. The documentation for HttpWebRequest.Timeout states the following:
Gets or sets the time-out value in milliseconds for the GetResponse
and GetRequestStream methods.
So you could wait up to the time-out to make the connection to POST the query and then up to the time-out again to start receiving a response. Once you start receiving a response the timeout becomes irrelevant and is not respected by the code that processes the response.
Therefore if you want a hard timeout you are better off implementing it yourself, longer term this may be something we can add to dotNetRDF but this is more complex to implement that simply fixing the bug about the timeout not getting honoured for the HTTP request.

Related

Connecting to Neo4j Aura with .NET Core 2.2 web api

I am trying to connect a to Neo4j Aura instance from a .NET core 2.2 web api. I understand I need the Neo4j .Net Driver v4.0.0-alpha01, but I do not seem to be able to connect. There aren't very many examples out there as this driver is new and so is Aura.
I keep getting:
Failed after retried for 6 times in 30000 ms. Make sure that your database is online and retry again.
I configure the driver as such
public void ConfigureServices(IServiceCollection services)
{
string uri = "neo4j://1234567.databases.neo4j.io:7687";//not actual subdomain
string username = "neo4j";
string password = "seeeeeeecret";//not actual password
services.AddCors();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton(GraphDatabase.Driver(uri, AuthTokens.Basic(username, password)));
}
and in my test controller i run this
private async Task<string> Neo4JTestAsync()
{
string db = "MyDb";
string message = "TESTMESSAGE";
IAsyncSession session = _driver.AsyncSession(o => o.WithDatabase(db));
try
{
var greeting = session.WriteTransactionAsync(async tx =>
{
var result = tx.RunAsync("CREATE (a:Greeting) " +
"SET a.message = $message " +
"RETURN a.message + ', from node ' + id(a)",
new { message });
var res = await result;
return "return something eventually";
});
return await greeting;
}
catch (Exception e)
{
return e.Message; // throws "Failed after retried for 6 times in 30000 ms. Make sure that your database is online and retry again"
}
finally
{
await session.CloseAsync();
}
}
I can't get the exact error message you do - but I'm pretty sure this is due to encryption - one of the big differences between the 1.x and 4.x drivers is the default position on Encryption - which is now off by default.
So you'll want to change your initialisation to:
services.AddSingleton(GraphDatabase.Driver(uri, AuthTokens.Basic(username, password), config => config.WithEncryptionLevel(EncryptionLevel.Encrypted)));
That should get you going. Also - make sure you stick with the neo4j:// protocol, as that'll route you properly.
Have you tried bolt:// in the connection string?
string uri = "bolt://1234567.databases.neo4j.io:7687";//not actual subdomain

Modifying list and sending it to front end sends original list

I couldn't phrase my Title better, but what's happening is: I fetch a list when the page opens, but it should remove one member of it based on something the user does on the page.
I'm using a ui:repeat in the front-end, and the getter method of this list is:
public List<Employee> getAvailableLoaders() {
if (availableLoaderList != null) {
List<Employee> availableLoaderListWithoutDriver = new ArrayList(availableLoaderList);
if (selectedDriver != null) {
logInfo("SelectedDriver is: " + selectedDriver.getName());
logInfo("List's size before removal is: " + availableLoaderListWithoutDriver.size());
logInfo("Removal was successfull? " + availableLoaderListWithoutDriver.remove(selectedDriver));
logInfo("List's size after removal is: " + availableLoaderListWithoutDriver.size());
}
return availableLoaderListWithoutDriver;
}
return null;
}
And my logs say:
Info: SelectedDriver is: [Driver name]
Info: List's size before removal is: 5
Info: Removal was successful? true
Info: List's size after removal is: 4
But it still shows a list with the driver removed and 5 members anyway. I know the getter is being called with the correct info and when the place that shows it is called because I'm watching the logs.
Any explanation or suggestion on how should I do this or am I just doing a really stupid mistake?

Neo4jClient Transaction Error

When I try to update an existing Node in Neo4JClient without using transaction, which has a Unique Constraint on Id and Name property, I receive this Exception:
CypherExecutionException: Node 6 already exists with label User and property "Name"=[Mike]
But when I try to update a Node in transaction, I receive this Exception:
Received an unexpected HTTP status when executing the request.
The response status was: 404 Not Found
The response from Neo4j (which might include useful detail!) was: {"results":[],"errors":[{"code":"Neo.ClientError.Transaction.UnknownId","message":"Unrecognized transaction id. Transaction may have timed out and been rolled back."}]}
My Code is like this:
using (var transaction = client.BeginTransaction())
{
client.Cypher
.Merge("(user:User { Id: {id}})")
.OnCreate().Set("user = {user}")
.OnMatch().Set("user = {user}")
.WithParams(new { id = user.Id, user = user })
.ExecuteWithoutResults();
transaction.Commit();
}
My question is: Is there a way to get the actual error when using transaction, like I get one when I don't use transaction?
Thanks
EDIT 1:
It looks like the NULL Id in not being handled correctly. After drilling down, I managed to pull the actual error. The first exception in is:
errors = {[
{
"code": "Neo.ClientError.Statement.InvalidSemantics",
"message": "Cannot merge node using null property value for Id"
}
]}
The stack trace for this exception is:
at Neo4jClient.Serialization.CypherJsonDeserializer1.GetRootResultInTransaction(JObject root) in [PATH_HIIDEN]\Neo4jClient\Serialization\CypherJsonDeserializer.cs:line 316
at Neo4jClient.Serialization.CypherJsonDeserializer1.CheckForErrorsInTransactionResponse(String content) in [PATH_HIIDEN]\Neo4jClient\Serialization\CypherJsonDeserializer.cs:line 291
at Neo4jClient.GraphClient.<>c__DisplayClass84_01.<PrepareCypherRequest>b__0(Task1 responseTask) in [PATH_HIIDEN]\Neo4jClient\GraphClient.cs:line 933
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
And after that there is the exception with 404 NOT Found error. For that the stack trace is:
at Neo4jClient.Execution.HttpResponseMessageExtensions.EnsureExpectedStatusCode(HttpResponseMessage response, String commandDescription, HttpStatusCode[] expectedStatusCodes) in [PATH_HIIDEN]\Neo4jClient\Execution\HttpResponseMessageExtensions.cs:line 41
at Neo4jClient.Execution.HttpResponseMessageExtensions.EnsureExpectedStatusCode(HttpResponseMessage response, HttpStatusCode[] expectedStatusCodes) in [PATH_HIIDEN]\Neo4jClient\Execution\HttpResponseMessageExtensions.cs:line 14
at Neo4jClient.Execution.ResponseBuilder.<>c__DisplayClass21_0.b__0(Task1 requestTask) in [PATH_HIIDEN]\Neo4jClient\Execution\ResponseBuilder.cs:line 127
at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Can this error be fixed in the next release please?
EDIT 2
using (var transaction = client.BeginTransaction())
{
manifest.Users = client.Cypher
.Merge("(n:User { Id: '8be0b8fd-c433-44d3-a7e2-3f0d1a03fefa'}) " +
"ON CREATE " +
"SET n = { " +
"Id: '8be0b8fd-c433-44d3-a7e2-3f0d1a03fefa', " +
"UserName: 'test.user', " +
"Name: 'Test User', " +
"Active: true " +
"} " +
"ON MATCH " +
"SET n = { " +
"Id: '8be0b8fd-c433-44d3-a7e2-3f0d1a03fefa', " +
"UserName: 'test.user', " +
"Name: 'Test User', " +
"Active: true " +
"}")
.Return(n => n.As<User>())
.Results
.ToList();
transaction.Commit();
}
Meaning fluent Api is causing the error.
So the problem is that in Neo4j 2.2.6 a change was introduced whereby any ClientError (Constraint Violation etc) resulted in an automatic Rollback of a transaction on the server.
Neo4jClient already did an auto-rollback on error, and continued to do so. With 2.2.6 this resulted in a 404 error as the transaction was no longer there (previous versions were fine).
So, I've made a fix and published an updated Nuget package, so version 1.1.0.12 of Neo4jClient will now give you the correct response.

Parse cloud code object.save() not working

I’m new to Parse and Cloud Code. I’m trying to initialize a new user’s data via a cloud code function. The function queries my “initial items objects” (a total of 223 items), then loops through each one creating a new “item” for the user. Inside the loop the new item’s fields are set and ends by calling save(). The function seems to be working, however, instead of saving 223 new items, only 9 are created. I placed a log statement after the save to see if in fact that loop was iterating through the 223 items … and it is. Below is my cloud code and log.
Any thoughts as to why only 9 items are being saved? The 9 items that are saved look fine.
Parse.Cloud.define("initializeNewUser", function(request, response) {
var InitialItemsObject = Parse.Object.extend("InitialParseItems");
var itemsQuery = new Parse.Query(InitialItemsObject);
itemsQuery.limit(500);
itemsQuery.find().then(function(results) {
console.log('Found ' + results.length +' Items.');
var user = request.user;
var ACL = new Parse.ACL(user);
for (var i = 0; i < results.length; i++) {
var defaultItem = results[i];
var item = new Parse.Object("Items");
item.set('itemID', defaultItem.get('itemID'));
item.set('author', user);
item.set('groupID', defaultItem.get('groupID'));
item.set('itemName', defaultItem.get('itemName'));
item.set('itemNote', defaultItem.get('itemNote'));
item.set('itemChecked', defaultItem.get('itemChecked'));
item.set('itemIsFavorite', defaultItem.get('itemIsFavorite'));
item.set('itemSelected', defaultItem.get('itemSelected'));
item.set('itemStruckOut', defaultItem.get('itemStruckOut'));
item.set('manualSortOrder', defaultItem.get('manualSortOrder'));
item.set('productID', defaultItem.get('productID'));
item.set('itemTimestamp', defaultItem.get('itemTimestamp'));
item.setACL(ACL);
item.save();
//console.log(defaultItem.get('itemName') + ' saved.');
}
// success has been moved inside the callback for query.find()
console.log('Successfully initialize ' + results.length + ' Items.');
response.success(results.length);
},
function(error) {
// Make sure to catch any errors, otherwise you may see a "success/error not called" error in Cloud Code.
console.log('Failed to initialize Items. Error code: ' + error.code + ' ' + error.message);
response.error("Failed to initialize Items. Error code: " + error.code + ": " + error.message);
});
});
I2015-07-17T15:16:38.661Z]v22 Ran cloud function initializeNewUser for user va0TTGwOk7 with:
Input: {}
Result: 223
I2015-07-17T15:16:38.930Z]Found 223 Items.
I2015-07-17T15:16:39.141Z]Successfully initialize 223 Items.
Ok I have a few comments.
1.) on line 7 you set user with request.user. I'm not sure how you are sending your data, but I've always used request.params.[insert item name]
2.) The next big thing is that by using "then()" you are using promises. at the end of every "then", you should be returning a promise. That means you wouldn't say "item.save()" but "return item.save()". Saving is a promise also so you will need to return those. Here should be your general promise pattern.
SomePromise.then( function(a)
{return promiseA} ).then( function(b)
{return PromiseB} ).then( function(c)
{response.success}, function(error){response.error})
3.) When you are saving lots of items, you either have to use "saveAll()" or make an array of promises, then save them all at once. A good rule of thumb is to use a promise array every time you are saving lots of things in an array. Here is the portion of the Cloud Code developer guide that will show you the correct format https://parse.com/docs/js/guide#promises-promises-in-parallel

Async web service call in custom list adapter

I have a problem when calling web service asynchronously in Monodroid. The call seems to work correctly, but the application crashes everytime when I process the response. Program behaves differently when I run it in emulator and when I run it on my phone (Samsung Galaxy S w 2.2.1 FROYO.XWJS8). I am thinking of creting the Threads by myself, but don't know if it solves the problem. The same code works fine when used in Windows Phone 7 application.
The code for Async method call is (note: in reality ShowMessage calls write to Android.Util.Log.Debug)
private void callws(string _input)
{
MessageBox.GetInstance().ShowMessage("Search async started, input: " + _input);
m_waitingrequest = new RequestStatus() { Waiting = true, NewestInput = _input, OriginalInput = _input };
connectormobile.UserInformation ui = new connectormobile.UserInformation()
{ UserName = m_appsettings.GetValue<string>(AppSettings.WS_USERNAME_NAME), Password = m_appsettings.GetValue<string>(AppSettings.WS_PASSWORD_NAME) };
MessageBox.GetInstance().ShowMessage("Username: " + ui.UserName + " Password: " + ui.Password);
m_client.SearchAsync(ui, _input);
MessageBox.GetInstance().ShowMessage("After search async call, input: " + _input);
}
Search Async result function starts with:
void m_client_SearchCompleted(object sender, connectormobile.SearchCompletedEventArgs e)
{
MessageBox.GetInstance().ShowMessage("Search async completed");
SearchCache.CacheElement element = new SearchCache.CacheElement();
element.SearchCriteria = m_waitingrequest.OriginalInput;
element.PartialResponse = e.Result.PartialResponse;
if (e.Result.CompanyNameInfoArray == null)
element.Rows = new List<connectormobile.CompanyNameInfo>();
else
element.Rows = e.Result.CompanyNameInfoArray.ToList();
MessageBox.GetInstance().ShowMessage("Search async returned, partial response: " + e.Result.PartialResponse
+ " row count: " + element.Rows.Count + " return value: " + e.Result.ReturnValue.ErrorDescriptionFi);
}
This is where the program behaves differently. In emulator the code never reaches the first line of SearchCompleted. But in my phone the SearchCompleted function seems goes through (at least all my debug lines are in trace), but after that the user interface freezes. (and after minute it says process not responding)
You are likely trying to modify the UI from the background thread instead of the UI thread. Use RunOnUIThread () to do your UI logic on the correct thread:
http://mono-android.net/Documentation/Guides/Writing_Responsive_Applications

Resources