Result.to(NodeEntity.class): stack overflow error - neo4j

while trying to retrieve my nodes back to my domain objects I'm getting this strange error:
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.ch.NativeThreadSet.remove(NativeThreadSet.java:76)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:678)
at org.neo4j.kernel.impl.nioneo.store.PersistenceRow.readFullWindow(PersistenceRow.java:158)
at org.neo4j.kernel.impl.nioneo.store.PersistenceRow$State$1.transition(PersistenceRow.java:115)
at org.neo4j.kernel.impl.nioneo.store.PersistenceRow.lock(PersistenceRow.java:59)
at org.neo4j.kernel.impl.nioneo.store.PersistenceWindowPool.acquire(PersistenceWindowPool.java:193)
at org.neo4j.kernel.impl.nioneo.store.CommonAbstractStore.acquireWindow(CommonAbstractStore.java:520)
at org.neo4j.kernel.impl.nioneo.store.NodeStore.getRecord(NodeStore.java:76)
at org.neo4j.kernel.impl.nioneo.xa.ReadTransaction.nodeLoadProperties(ReadTransaction.java:239)
at org.neo4j.kernel.impl.persistence.PersistenceManager.loadNodeProperties(PersistenceManager.java:113)
at org.neo4j.kernel.impl.core.NodeManager.loadProperties(NodeManager.java:682)
at org.neo4j.kernel.impl.core.NodeImpl.loadProperties(NodeImpl.java:132)
at org.neo4j.kernel.impl.core.Primitive.ensureFullProperties(Primitive.java:584)
at org.neo4j.kernel.impl.core.Primitive.ensureFullProperties(Primitive.java:567)
at org.neo4j.kernel.impl.core.Primitive.getProperty(Primitive.java:153)
at org.neo4j.kernel.impl.core.NodeImpl.getProperty(NodeImpl.java:51)
at org.neo4j.kernel.impl.core.NodeProxy.getProperty(NodeProxy.java:155)
at org.springframework.data.neo4j.support.typerepresentation.AbstractIndexingTypeRepresentationStrategy.readAliasFrom(AbstractIndexingTypeRepresentationStrategy.java:106)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:26)
at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:96)
at org.springframework.data.convert.DefaultTypeMapper.getDefaultedTypeToBeUsed(DefaultTypeMapper.java:144)
at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:121)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:76)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:180)
at org.springframework.data.neo4j.fieldaccess.RelationshipNodeFieldAccessorFactory$RelationshipNodeFieldAccessor.getValue(RelationshipNodeFieldAccessorFactory.java:102)
at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:90)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:40)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:61)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:207)
at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:57)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:100)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:92)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:192)
at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:180)
at org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper.underlyingObjectToObject(GraphBackedEntityIterableWrapper.java:41)
at org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper.underlyingObjectToObject(GraphBackedEntityIterableWrapper.java:27)
at org.neo4j.helpers.collection.IterableWrapper$MyIteratorWrapper.underlyingObjectToObject(IterableWrapper.java:57)
at org.neo4j.helpers.collection.IteratorWrapper.next(IteratorWrapper.java:47)
at org.neo4j.helpers.collection.IteratorUtil.addToCollection(IteratorUtil.java:324)
at org.neo4j.helpers.collection.IteratorUtil.addToCollection(IteratorUtil.java:341)
at org.springframework.data.neo4j.fieldaccess.RelatedToViaCollectionFieldAccessorFactory$RelatedToViaCollectionFieldAccessor.getValue(RelatedToViaCollectionFieldAccessorFactory.java:122)
at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
For example, this is my cypher query which tries to get a user node entity:
public MyUser getUserByUserId(String userId){
Long t1 = System.currentTimeMillis();
if(existsUserByUserId(userId)){
HashedMap params = new HashedMap();
params.put("userId", userId);
String query = "START x=node:searchByUserId(userId = {userId})" +
" RETURN x";
Result<Map<String,Object>> result = neo4jTemplate.query(query, params);
MyUser user = result.to(MyUser.class).single();
Long t2 = System.currentTimeMillis();
logger.info("get user by user id exec time: " + (t2-t1) + " ms");
return user;
}
Long t2 = System.currentTimeMillis();
logger.info("get user by id exec time: " + (t2-t1) + " ms");
return null;
}
where searchByUserId is a node index and existsUserByUserId is a helper method which checks whether the the specified user exists or not. The problem is that when I try to call the result.to() method I get this error randomly. With randomly I mean that I'm not getting this error always. To be more concrete, I inserted all my node/relationships using the native Neo4j Java API and now I'm trying to retrieve these objects with Spring Data Neo4j (repository approach). This is how I'm inserting my node entities:
public Node createAndIndexMyUserNode(MyUser user){
Map<String,Object> properties = new HashMap<String, Object>();
properties.put("userId", user.getUserId());
properties.put("baseID" , user.getBaseID());
properties.put("__type__", MyUser.class.getName());
properties.put("canUpdate", false);
Node node = neo4jTemplate.getOrCreateNode("searchByUserId", "userId", user.getTwitterId(), properties);
return node;
}
This is probably the problem but I don't know how I could solve it. I suspect that bad database shutdowns or relationships creation (afterwards) could be also one reason, but I'm not sure though.
This is the reason why I'm inserting "manually" all nodes instead of using the repository save method. Any suggestions or ideas?
Thank you all in advance!

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?

Azure ConnectionString using totally wrong login credentials

I have created an ASP.NET MVC website which accesses a database, both of which are hosted in azure. I have created a specific login and user to access the database and can see them in SSMS. I see the login screen for the website. The trouble is when I try to log in, the login fails telling me that the user cannot be found.
Here's where it starts getting strange. The login ID that cannot be found is not the login that I created or that which is in my web.config (or my app.config). Instead it is the master user that I have to access the db server and other areas of my azure resources.
Here is the section from the web.config on my PC that was used to publish to azure:
<connectionStrings>
<add name="CSSL" connectionString="Server=tcp:svrmysqlserver.database.windows.net,1433;Database=MyDatabase;User ID=sqlUser#svrmysqlserver;Password=q2wW£E;Encrypt=True;TrustServerCertificate=False;Connection Timeout=300;" providerName ="System.Data.SqlClient" />
</connectionStrings>
The Error message said
Login failed for user 'MasterUser'.
I added some debug code to get the connection strings that were available and being used on the server and tried again. I got the following:
Login failed for user 'MasterUser'.
ConfigurationManager[data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true]
ConfigurationManager[]
ConfigurationManager[Data Source=tcp:svrmysqlserver.database.windows.net,1433;Initial Catalog=MyDatabase;User ID=MasterUser#svrmysqlserver;Password=MasterPassword;Connect Timeout=30;Encrypt=True;TrustServerCertificate=False]
ConfigurationManager[Data Source=tcp:svrmysqlserver.database.windows.net,1433;Initial Catalog=MyDatabase;User ID=MasterUser#svrmysqlserver;Password=MasterPassword;Connect Timeout=30;Encrypt=True;TrustServerCertificate=False]
Database[Data Source=tcp:svrmysqlserver.database.windows.net,1433;Initial Catalog=MyDatabase;User ID=MasterUser#svrmysqlserver;Password=MasterPassword;Connect Timeout=30;Encrypt=True;TrustServerCertificate=False]
I attach a code snippet in answer to the comment below - where did I get the output from? I added code in the handler to get the current values of the connection strings before returning. The function is on the data context and is the first attempt to read data from the database.
public ReturnInfo GetSystemRecord(out AppSystemRecord rec)
{
ReturnInfo ret = null;
rec = new AppSystemRecord();
//check that we have a hope of seeing this record - no records should have an ID of zero
try
{
var query = from x in SystemRecord select x; // ther should only ever be one
if (query.Count() == 0)
{
SetupAdminUser();
SystemRecord.Add(rec);
SaveChanges();
}
else
rec = query.FirstOrDefault();
ret = new ReturnInfo(); // defaults to all okay
if (rec.Status == SystemStatus.Created || rec.Status == SystemStatus.Updated)
SetupDefaultsForLookupTables();
rec.Status = SystemStatus.Normal;
SaveChanges();
}
catch (Exception e)
{
while (e.InnerException != null)
e = e.InnerException;
ret = new ReturnInfo(ErrorType.UnableToReadDomainObject, e.Message + "\r\n");
// THIS IS THE IMPORTANT BIT TO GET THE CONNECTION DATA
foreach (ConnectionStringSettings v in ConfigurationManager.ConnectionStrings)
{
ret.ErrorMessage += "ConfigurationManager[" + v.ConnectionString + "] " + "\r\n";
}
ret.ErrorMessage += "Database[" + Database.Connection.ConnectionString + "] " + "\r\n";
}
return ret;
}
I searched my solution for any uses of MasterUser in any file but found none, nor any references to the password, which was the correct MasterUser password.
I deleted both the website and database and started again to be sure there were no files lying around in the cloud but without any joy. Any ideas what is going on? Is there some sort of cache that I haven't invalidated? is there some rule whereby there must be a connection to the master?
Your help and ideas would be gratefully received.
Craig

exception inside of OnException

I created a custom attribute, inheriting from HandleErrorAttribute:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
try
{
Utility.LogAndNotifyOfError(filterContext.Exception, null, true);
}
catch(Exception ex)
{
filterContext.Exception = ex;
}
}
}
, and then registered with:
filters.Add(new CustomHandleErrorAttribute());
This has always worked as intended. However a common problem with my log method is that it uses a custom event log source when writing to the event log, which the app pool account typically doesn't have the permissions to create. Creating the event log source is a simple powershell script, however I wanted to actually include that tidbit in the error:
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
The problem is that the catch in the OnException is never hit. When debugging, the custom error I throw from LogAndNotifyOfError instead triggers a second call to OnException, and the detail of my ErrorHandlerException is never seen. I want the asp.net error page that comes up to be with my custom error detail rather than the SecurityException that was originally raised.
You can even see the surrounding try in the displayed error:
Edit: Entire log method listed:
public static void LogAndNotifyOfError(Exception ex, String extraInfo, Boolean sendEmail)
{
//if the error handler itself faulted...
if (ex is ErrorHandlerException)
return;
string eventLogName = "Application";
string eventLogSource = "MySourceName";
String error = ex.ToString();
if (error.Length > 28000)
error.Substring(0, 28000);//event log is limited to 32k
error += "\r\n\r\nAdditional Information: \r\n"
+ "Machine Name: " + Environment.MachineName + "\r\n"
+ "Logged in user:" + App.CurrentSecurityContext.CurrentUser?.UserId + "\r\n"
+ extraInfo + "\r\n";
EventLog log = new EventLog(eventLogName);
log.Source = eventLogSource;
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{//this doesn't work - for some reason, OnError still reports the original error.
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
//if the email-to field has been set...
if (!String.IsNullOrEmpty(App.Config.General.ErrorHandlerSendToAddresses) && sendEmail)
{
//...then send the email
MailMessage email = new MailMessage();
email.To.Add(App.Config.General.ErrorHandlerSendToAddresses);
email.IsBodyHtml = false;
email.Subject = String.Format("Error in {0}", eventLogSource);
email.Body = email.Subject + "\r\n\r\n"
//+ "Note: This error may be occuring continuously, but this email is only sent once per hour, per url, in order to avoid filling your mailbox. Please check the event log for reoccurances and variations of this error.\r\n\r\n"
+ "The error description is as follows: \r\n\r\n"
+ error + "\r\n\r\n";
SmtpClient smtp = new SmtpClient();
smtp.Send(email);
}
}
I figured it out (sort of). It would appear that when the newly throw exception has an inner exception, it is only displaying that inner exception. It does not matter what the type is on the outer or inner exception.

neo4j 2.0 findNodesByLabelAndProperty not working

I'm currently trying the Neo4j 2.0.0 M3 and see some strange behaviour. In my unit tests, everything works as expected (using an newImpermanentDatabase) but in the real thing, I do not get results from the graphDatabaseService.findNodesByLabelAndProperty.
Here is the code in question:
ResourceIterator<Node> iterator = graphDB
.findNodesByLabelAndProperty(Labels.User, "EMAIL_ADDRESS", emailAddress)
.iterator();
try {
if (iterator.hasNext()) { // => returns false**
return iterator.next();
}
} finally {
iterator.close();
}
return null;
This returns no results. However, when running the following code, I see my node is there (The MATCH!!!!!!!!! is printed) and I also have an index setup via the schema (although that if I read the API, this seems not necessary but is important for performance):
ResourceIterator<Node> iterator1 = GlobalGraphOperations.at(graphDB).getAllNodesWithLabel(Labels.User).iterator();
while (iterator1.hasNext()) {
Node result = iterator1.next();
UserDao.printoutNode(emailAddress, result);
}
And UserDao.printoutNode
public static void printoutNode(String emailAddress, Node next) {
System.out.print(next);
ResourceIterator<Label> iterator1 = next.getLabels().iterator();
System.out.print("(");
while (iterator1.hasNext()) {
System.out.print(iterator1.next().name());
}
System.out.print("): ");
for(String key : next.getPropertyKeys()) {
System.out.print(key + ": " + next.getProperty(key).toString() + "; ");
if(emailAddress.equals( next.getProperty(key).toString())) {
System.out.print("MATCH!!!!!!!!!");
}
}
System.out.println();
}
I already debugged through the code and what I already found out is that I pass via the InternalAbstractGraphDatabase.map2Nodes to a DelegatingIndexProxy.getDelegate and end up in IndexReader.Empty class which returns the IteratorUtil.EMPTY_ITERATOR thus getting false for iterator.hasNext()
Any idea's what I am doing wrong?
Found it:
I only included neo4j-kernel:2.0.0-M03 in the classpath. The moment I added neo4j-cypher:2.0.0-M03 all was working well.
Hope this answer helps save some time for other users.
#Neo4j: would be nice if an exception would be thrown instead of just returning nothing.
#Ricardo: I wanted to but I was not allowed yet as my reputation wasn't good enough as a new SO user.

Resources