I want to update an existent Node in the db.
I could correctly create a node but can't update an existent one.
try (Transaction tx = template.getGraphDatabaseService().beginTx()) {
Node node = repository.findNodeUsingId("n1");
if(node != null){
//Modify some properties using setProperty
node.setProperty("name","P");
//How should I do to save the modified node object?
}else{
//Create the node
//This part works fine
node = template.createNode();
node.setProperty("name", "T");
}
tx.success();
}
You don't have to save the modified object.
Once setProperty has been called, your node property has been set in the current Transaction.
The only thing you are missing here is to close the Transaction, check this (from Neo4j Javadoc) about Transaction.close():
Commits or marks this transaction for rollback, depending on whether
success() or failure() has been previously invoked. All
ResourceIterables that where returned from operations executed inside
this transaction will be automatically closed by this method. This
method comes from AutoCloseable so that a Transaction can participate
in try-with-resource statements. It will not throw any declared
exception. Invoking this method (which is unnecessary when in
try-with-resource statement) or finish() has the exact same effect.
Related
This is the overall flow of my setup:
void ScriptPlayer::new_lua_state() {
lua = {};
lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::coroutine, sol::lib::math);
[...]
// Proceeds to initialize the state with usertype definitions and values
}
void ScriptPlayer::play(std::string path) {
main_coroutine = sol::nil;
script_env = sol::environment(lua, sol::create, lua.globals());
auto result = lua.load_file(path);
main_coroutine = sol::coroutine(result);
script_env.set_on(main_coroutine);
}
void ScriptPlayer::update() {
if (main_coroutine) {
main_coroutine();
}
}
"new_lua_state" is called once at the beginning of everything, then "play" is called anytime I want to execute a new lua script (that yields). "update" is executed every frame, and progresses the coroutine until it's finished, at which point it stops.
The problem:
If I call "play" while the previous script coroutine has yielded but hasn't yet finished, I expect lua to discard the whole environment and create a new one, discard the old coroutine, parse the script again, create a brand new coroutine and start its execution from the beginning.
What I get instead is that the coroutine will STILL be running from the state of the previous script's coroutine (which should be completely discarded) and not from the very beginning.
How is this possible? Where exactly is the state of the coroutine stored?
I tried wrapping the state with a thread, I tried calling lua.clear_stack but nothing made any difference in the fact that the new coroutine never starts from the beginning of the function when I re-parse the script and re-create the sol::coroutine object.
Any clarification is hugely appreciated.
Here was the solution:
https://github.com/ThePhD/sol2/issues/1061
Apparently my attempt of wrapping the state with a thread was faulty, because that was exactly the thing to do.
So to solve this, here's what I did:
void ScriptPlayer::play(std::string path) {
script_env = sol::nil;
main_coroutine = sol::nil;
script_thread = sol::thread::create(lua);
script_env = sol::environment(script_thread.state(), sol::create,
script_thread.state().globals());
auto result = script_thread.state().load_file(path);
main_coroutine = sol::coroutine(result);
script_env.set_on(main_coroutine);
}
One this that still blows my mind is that if I remove the second line (that reset the C-held reference to the lua coroutine), the result goes back to wrongly resuming the previous coroutine, despite that very same variable being set to a different value shortly after..
This baffles me deeply.
I have a problem with transactions. The data in the transaction is always null and the update handler is called only singe once. The documentation says :
To accomplish this, you pass transaction() an update function which is
used to transform the current value into a new value. If another
client writes to the location before your new value is successfully
written, your update function will be called again with the new
current value, and the write will be retried. This will happen
repeatedly until your write succeeds without conflict or you abort the
transaction by not returning a value from your update function
Now I know that there is no other client accessing the location right now. Secondly if I read the documentation correctly the updateCounters function should be called multiple times should it fail to retrieve and update data.
The other thing - if I take out the condition if (counters === null) the execution will fail as counters is null but on a subsequent attempt the transaction finishes fine - retrieves data and does the update.
simple once - set on this location work just fine but it is not safe.
Please what do I miss?
here is the code
self.myRef.child('counters')
.transaction(function updateCounters(counters){
if (counters === null) {
return;
}
else {
console.log('in transaction counters:', counters);
counters.comments = counters.comments + 1;
return counters;
}
}, function(error, committed, ss){
if (error) {
console.log('transaction aborted');
// TODO error handling
} else if (!committed){
console.log('counters are null - why?');
} else {
console.log('counter increased',ss.val());
}
}, true);
here is the data in the location
counters:{
comments: 1,
alerts: 3,
...
}
By returning undefined in your if( ... === null ) block, you are aborting the transaction. Thus it never sends an attempt to the server, never realizes the locally cached value is not the same as remote, and never retries with the updated value (the actual value from the server).
This is confirmed by the fact that committed is false and the error is null in your success function, which occurs if the transaction is aborted.
Transactions work as follows:
pass the locally cached value into the processing function, if you have never fetched this data from the server, then the locally cached value is null (the most likely remote value for that path)
get the return value from the processing function, if that value is undefined abort the transaction, otherwise, create a hash of the current value (null) and pass that and the new value (returned by processing function) to the server
if the local hash matches the server's current hash, the change is applied and the server returns a success result
if the server transaction is not applied, server returns the new value, client then calls the processing function again with the updated value from the server until successful
when ultimately successful, and unrecoverable error occurs, or the transaction is aborted (by returning undefined from the processing function) then the success method is called with the results.
So to make this work, obviously you can't abort the transaction on the first returned value.
One workaround to accomplish the same result--although it is coupled and not as performant or appropriate as just using the transactions as designed--would be to wrap the transaction in a once('value', ...) callback, which would ensure it's cached locally before running the transaction.
Below is a part of code where the discrepancy is seen. The problem that i am scratching my head over is this. I am confused over why the state of a registration changes from Active to Inactive after leaving the withNewSession block.
Transaction.withNewSession{ session ->
if (saleId?.isLong()){
invoices = registrationService.completeSale(saleId.toLong(), transactionResponse)
}
println Registration.last().status //ACTIVE
}
println Registration.last().status //INACTIVE
The following part is optional but just for reference i have pasted it. One thing the completeSale method does is it calls the following method which will activate registrations.
void activateRegistrations(SaleInvoice invoice){
Assert.notNull(invoice, 'SaleInvoice cannot be null when activating registrations.')
List<Registration> registrations = this.findRegistrationsBySaleInvoice(invoice)
registrations.each{
it.status = EntityStatus.ACTIVE
it.save()
}
}
Now, my doubt is why the first println statement i.e println Registration.last().status //ACTIVE will print ACTIVE and the second will print INACTIVE. I am guessing the Transaction.withNewSession has to do with it since the change happens in the boundary of
the withNewSession block. How can withNewSession be responsible for this apparent discrepancy?
I need to make a series of database queries that each return a stream of results. Once all the information is collected and sent the 'complete' message needs to be send last. In my code 'sendCompleteMessageToClient' gets sent first.
Future.forEach(centerLdapNames.values, (center) {
db
.collection(center)
.find({'date': {'\$gte': json['from'], '\$lt': json['to']}})
.forEach(sendAppointmentToClient);
}).whenComplete(() => sendCompleteMessageToClient("all"));
How do I wait for all 'sendAppointmentToClient' to finish properly?
I guess you just miss the return of the future
Future.forEach(centerLdapNames.values, (center) {
return db // <== always return the returned future from calls to async functions to keep the chain connected
.collection(center)
.find({'date': {'\$gte': json['from'], '\$lt': json['to']}})
.forEach(sendAppointmentToClient);
}).whenComplete(() => sendCompleteMessageToClient("all"));
If you use wait these calls might be executed in parallel instead of one after the other
Future.wait(centerLdapNames.values.map((center) { ...}, eagerError: false)
.whenComplete((...))
Consider the following code:
if (!serpKeyword) {
serpKeyword = new SerpKeyword(
keyword: searchKeyword,
geoKeyword: geoKeyword,
concatenation: concatenation,
locale: locale
)
serpKeyword.save(flush: true, failOnError: true)
}
serpService.submitKeyword(serpKeyword, false)
Here's the submitKeyword method:
#Transactional(propagation = Propagation.REQUIRES_NEW)
boolean submitKeyword(keywordToSubmit, boolean reset) {
def keyword = SerpKeyword.get(keywordToSubmit.id)
No error is raised when I call serpKeyword.save, but when I get into the submitKeyword method, SerpKeyword.get(keywordToSubmit.id) returns null. What could be preventing this from saving?
Edit
Changing REQUIRES_NEW to REQUIRED seems to do the trick. Here's what I think is happening.
The code that calls serpService.submitKeyword is located within a service method. From what I understand, service method's have a default propagation strategy of REQUIRED. Since all this database writes are happening within the context of a transaction, the writes are queued up in the database, but not actually executed against the database until the transaction is completed, according to the docs:
Note that flushing is not the same as committing a transaction. If
your actions are performed in the context of a transaction, flushing
will execute SQL updates but the database will save the changes in its
transaction queue and only finalize the updates when the transaction
commits.
We call serpService.submitKeyword before our transaction is actually finished. That method starts a completely new transaction where our serpKeyword is not available. Changing it to REQUIRED works because we are now operating within the context of our parent transaction.
I think some part of the stack is being lazy. I've ran into this behavior with Hibernate, if that's what you're using. I'm not sure it's an approved maneuver, but you could clear the session before calling submitKeyword like so:
long keywordId = serpKeyword.id
SerpKeyword.withSession{it.clear()}
serpService.submitKeyword(keywordId, false)
And then change the method to:
#Transactional(propagation = Propagation.REQUIRES_NEW)
boolean submitKeyword(keywordId, boolean reset) {
def keyword = SerpKeyword.get(keywordId)
Then I bet the .get() will work. If you have other objects in the session you need. You will need to lift those out by storing their id's and .get()ing them as well.