Transaction Block has wrong Return [duplicate] - ios

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.

Related

Tarantool blocks for operations, if error occurs inside box.begin()...box.commit() block

If error occurs inside box.begin()...box.commit() block, Tarantool box blocks for operations untill you manually execute box.commit()
For example, I provide incorrect data without one of fields to my function:
replace = function(space, data)
local id = data[1][1]
box.begin()
my_module.delete(space, id)
local response = my_module.insert(space, data)
box.commit()
return response
end
Inside of my_module.insert() it will encount an error, so execution of replace() will be aborted before execution of box.commit().
Later, if i call replace() again with another data, it won`t be able to execute box.begin() because of previously open transaction.
pcall(my_module.insert()) wasn`t able to catch an error.
There's a handy function for it - box.atomic
I suggest the following implementation:
replace = function(space, data)
return box.atomic(function()
local id = data[1][1]
my_module.delete(space, id)
return my_module.insert(space, data)
end)
end
pcall(my_module.insert()) wasn't able to catch an error.
pcall catches all errors. The error isn't caught probably because it occurs earlier on the delete operation.
By the way, Tarantool has inline operations for replace:
https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space_index/#box-space-replace

Zendesk - CreateUser JobStatus Results is Null

So I am currently working with the ZenDesk API. I am creating many users using the batch CreateUser method that takes an array of up to 100 user objects. Now, for some reason, some users fail to generate. Therefore, I wanted to obtain the result of the JobStatus after creating the users so that I can identify the problem easily. The issue is that the result variable is null after performing the CreateUsers() method.
Some sample code:
public static void createEndUsers(Zendesk zd){
for(Organization org : zd.getOrganizations()){
List<User> usersList = getUsersPerOrg(org, 0)
JobStatus js = zd.createUsers(usersList);
List<T> resultElements = js.getResults();
}
}
Why is getResults() returning null in this instance? Is it because the operation has not yet been performed when it reaches that part of the code? How can I make sure that I "wait" until the result is ready before I try to access it?
If you have a look at the posssible values from org.zendesk.client.v2.model.JobStatus.JobStatusEnum you will notice that the job may be queued for execution or it could still be running at the time that the job status was returned by the operation org.zendesk.client.v2.Zendesk#createUsers(org.zendesk.client.v2.model.User...).
As can be seen from the Zendesk Documentation for createUsers Operation
This endpoint returns a job_status JSON object and queues a background job to do the work. Use the Show Job Status endpoint to check for the job's completion.
only when the job is completed, there will be a corresponding result delivered for the operation.
A possible solution in your case would be to check (possibly in a separate thread) every 500ms whether the job status is not queued or not running (otherwise said whether it completed) and if it successfully completed to retrieve the results.

Update a Node object with Spring data

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.

Dart nested futures whenComplete fires first

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((...))

Why doesn't my GORM object save to the database?

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.

Resources