Progress-4gl: How does transaction scope apply to external program calling? - openedge

I need some help understanding transaction scoping for procedures/programs outside the current program.
Suppose I've three program, program A, program B and program C. Inside program A, I've a procedure that has some lines in it wrapped inside a do transaction (not strongly typed) block. Within that do transaction block, it calls another Program B. Upon return from program B there is an undo, leave command. Within the same transaction block, it calls program C and has an undo, leave after this call too.
My question is, if within the transaction block, program B executes without errors, but program c returned an error, will the undo,leave after program C call will also undo transactions that happened inside program B?
Procedure do_something:
some processing....
do transaction:
error-message = "".
{run programB.p}
if error-message <> "" then undo, leave.
some further processing...
error-message = "".
{run programC.p}
if error-message <> "" then undo, leave.
end. /* end of do transaction */
end procedure.

Yes. In the example that you describe everything gets rolled back.
It is not so much that it is "extended" per se but just that the transaction includes everything that happens in that session from the point in time when it is enabled all the way until it is either committed or rolled back. Internal procedures, external procedures, user defined functions, methods of classes, trigger code etc.
"In that session" is important - if you call a procedure on an app server that activity is NOT included since it is its own process with its own distinct transaction context.
When app servers are involved things get messy. The original caller has no (built-in) capability to know what to roll back in the called app server session. The app server call could return an error that causes the caller to roll back if it encounters problems but the caller could also decide to trap and ignore that error.

Yes. Everything happening in the transaction block will be undone.

Related

Can we compile procedure or function if there is DDL lock on that procedure?

procedure cannot be compiled? it is just running and nothing happened.
and I have try following code to check whether any block session make this. but I did not find any blocked session.
select
*
from
v$session
where
blocking_session is not NULL
order by
blocking_session;
You won't find it as a blocking session, nor in gv$lock, because it's a library object lock rather than a data object lock. Instead, look at gv$access and/or dba_ddl_locks. You can also look at gv$session for a plsql_object_id or plsql_entry_object_id matching the object_id of your procedure in either dba_objects or dba_procedures. That's not a sure-fire way of catching everything though, if you have chained PL/SQL calls... but gv$access and/or dba_ddl_locks will have what you need for sure.
If something is executing your procedure, you will have to wait for them to complete, or kill their session, before you can compile it. It's a weak point in Oracle, that prevents us from pushing code changes in without kicking everybody out first. But that's the way it is.
I found that there is a lot of rows in the table and whenever I try to compile procedure it can call that table too. So I Stop the compilation process at that time and execute the rollback statement. Therefore it takes time to rollback milions of rows one by one I have tracked those process through the following script.
select s.sid, s.serial#, s.client_info, t.addr, sum(t.used_ublk)
from v$transaction t, v$session s
where t.addr = s.taddr
group by s.sid, s.serial#, s.client_info, t.addr;
I just need to wait and might be I did not find any idea about to do anything except waiting. When all the transactions are rollbacked then I try to compile it and it compiled.

Ada: the select/else statement on protected entries. What does it do?

I have a doubt about Ada, and in particular about the select statement when used in conjunction with a protected entry. Let's consider the following code fragment:
select
Protected_Object.Some_Entry;
else
DoSomethingElse;
end select;
My question is simple: when the select statement is reached, what happens? In particular, what I want to know is: does the else branch get chosen only if Some_Entry's guard is closed or does it get chosen even if the guard is opened but the entry is "occupied" (ie: there's already a call to Some_Entry executing) and thus cannot be called immediately??
I believe that the else branch is chosen only if the entry's guard is closed (i.e. the barrier condition is false). The sequence of events for a protected entry call is given by RM 9.5.3(8):
A new protected action is started on the object.
The named entry is checked to see if it is open (i.e. the barrier condition is true); if open, the entry call is said to be selected immediately, and then the entry body is executed.
Starting a protected action may involve a short delay if another task is performing a protected action on the same object (9.5.1(4)). However, the intent is that this delay is always very short. If a protected subprogram or entry does anything that could block the program, this is considered an error (9.5.1(8-18)). Thus, waiting until another task releases a protected object is supposed to be a very short wait if at all; on a multi-processor system, it's perfectly acceptable to implement this wait by spinning (essentially while Protected_Object_Is_In_Use(Obj) loop null; end loop;) as opposed to waiting on a queue.
Thus, my reading of 9.5.3(8) is that the definition of "selected immediately" does not take into account the short wait needed if another task is engaged in a protected action on the object. If the task has to wait, it does so. If, once it is able to grab the object, it finds that the barrier is true, then the entry is "selected immediately". This may not quite fit our idea of what the English word "immediately" means, but it's how the term is defined.
Thus, for a conditional entry call, RM 9.7.3 says that the entry call is cancelled (and the else branch executed) if it is not selected immediately. Using the definition in 9.5.3(8), this means that the else branch is executed only if the barrier condition is false (after the task succeeds in grabbing the protected object).
The 'else' branch is chosen if Some_Entry is not immediately accessible (either because there is already another task accessing the protected object, or because the guard prevents calling the entry in the first place.
The goal of the 'else' branch is basically that your task does not stay blocked and thus might miss a timeout or an action to execute on a regular schedule. So when you use 'else', the select statement cannot be blocking (unless of course the entry itself, once called, takes forever).
Edit: As demonstrated below by #simonwright, this answer is incorrect: the 'else' branch is taken when the guard is False, otherwise the task will block on the protected object's entry. By design, such entries should perform their work in a very limited amount of time.

PetaPoco transaction not rolling back

Maybe this is a really noob question since I'm fairly new to handling transactions with PetaPoco.
Problem I'm facing using PetaPoco as a microORM to handle my db transaction is that if I throw an exception just before the .Complete() method of the transaction, everything is rolled back correctly but if I'm catching exceptions inside the
Using scope As PetaPoco.Transaction = db.GetTransaction()
' try/catch here and if the db command fails transaction won't roll back
scope.Complete()
End Using
the transaction won't roll back if one of the db operations fails. How can I solve this?
The issue was me not handling correctly the "call/not call" the scope.Complete() based on Exceptions intercepted along the path.
In particulart I had a boolean flag "rollBackTransaction" starting to false and then updating to true if any of the try/catch block inside the transaction raised and exception.
At the end I just checked it:
If Not rollBackTransaction Then
scope.Complete()
End If
This can be used as well for the TransactionScope suggested by Simon wich will eventually roll back a transaction if .Complete() is not called before closing the Using block.
Now what was causing a false flag and thus calling the scope.Complete() method each time, was that inside the transaction I called a sub wich had it's own exception handling and thus would never raise an exception in the main transaction block to correctly update the "rollBackTransaction" flag.
What I learned is that if you are using try/catch inside the transaction, be very sure that the external methods you call raise an exception if they fall, and update a flag all along based on wich you will call the scope.Complete().
Anyway Simon, thanks for pointing out that .NET feature I didn't know wich seems to be extremely useful!
Wonder what else it will include in the transaction... file system changes?

Is there a way to acquire a lock in Redis? (Node.js)

My Node.js application accepts connections from the outside. Each connection handler reads a SET on Redis, eventually modifies the set itself, then moves on. The problem is that in the meanwhile another async connection can try to read the same SET and try to update it or decide its next step based on what it reads.
I know that Redis does its best to be atomic, but this is not quite sufficient for my use case. Think about this: the set is read to understand if it's FULL (there is a business rule for that). If it's FULL, then something happens. The problem is that if there is one only slot left, two semi-concurrent connections could think each one is the last one. And I get an overflow.
I there a way to keep a connection "waiting" for the very short time the other eventually needs to update the set state?
I think this is a corner case, very very unluckely... but you know :)
Using another key as the "lock" is an option, or does it stink?
How about using blpop to do locking. blpop key 5 to wait 5 seconds for key. At start put item(to identify queue is not empty) at key. The connection acquiring the lock should remove item from key. The next connect then can't acquire the lock, because empty, but blpop has the following nice property:
Multiple clients can block for the same key. They are put into a
queue, so the first to be served will be the one that started to wait
earlier, in a first-BLPOP first-served fashion.
When connection which acquired lock has finished task it should put back item back in queue, then the next connection waiting can acquire lock(item).
You may be looking for WATCH with MULTI/EXEC. Here's the pattern that both threads follow:
WATCH sentinel_key
GET value_of_interest
if (value_of_interest = FULL)
MULTI
SET sentinel_key = foo
EXEC
if (EXEC returned 1, i.e. succeeded)
do_something();
else
do_nothing();
else
UNWATCH
The way this works is that all of the commands between MULTI and EXEC are queued up but not actually executed until EXEC is called. When EXEC is called, before actually executing the queued instructions it checks to see if sentinel_key has changed at all since the WATCH was set; if it has, it returns (nil) and the queued commands are discarded. Otherwise the commands are executed atomically as a block, and it returns the number of commands executed (1 in this case), letting you know you won the race and do_something() can be called.
It's conceptually similar to the fork()/exec() Unix system calls - the return value from fork() tells you which process you are (parent or child). In this case it tells you whether you won the race or not.

Can I call SQLExecute after SQLFreeStmt without SQLPrepare?

I have the following sequence of code calls:
SQLPrepare
SQLExecute(hstmt, SQL_CLOSE);
SQLFreeStmt
//It works till here
SQLExecute //Now it fails.
Why am I required to call SQLPrepare again, I just freed the cursor. I shouldn't prepare the SQL statement again.
The correct behaviour is that SQLPrepare/SQLExecute/SQLFreStmt(stmt, SQL_CLOSE) should allow another SQLExecute on the same stmt handle without re-preparing. You can see this as a valid state transition in the ODBC Programmers Guide. You might use this sequence if you had done a SQLPrepare(sql) and only fetched some of the rows (instead of all of them) as without the SQLFreeStmt(stmt, SQL_CLOSE) or fetching until SQL_NO_DATA was returned you'd get an invalid cursor state if you issued another SQLExecute. SQLFreeStmt(stmt, SQL_DROP) is equivalent to SQLFreeHandle(SQL_HANDLESTMT,stmt) and frees the entire stmt handle meaning you cannot use it again at all.
SQLFreeStmt(hstmt, SQL_CLOSE) cleans up everything to do with that statement handle, take a look at the summary:
SQLFreeStmt stops processing
associated with a specific statement,
closes any open cursors associated
with the statement, discards pending
results, or, optionally, frees all
resources associated with the
statement handle.
If you don't want to use SQLPrepare again you can use SQLExecDirect instead.

Resources