solidDB Help : Programming : SQL extensions : Stored procedures : Remote stored procedures in Advanced Replication configurations : Deferred procedures in Advanced Replication configurations
  
Deferred procedures in Advanced Replication configurations
Deferred procedures are stored procedures that are called after a commit is processed.
In Advanced Replication configurations, at the end of a committed transaction, you might want to perform a specific action. For example, if the transaction updated some data in a master publication, you might want to notify a replica server that the master data is updated. solidDB allows the START AFTER COMMIT statement to specify an SQL statement that is executed when the current transaction is committed, see START AFTER COMMIT. The specified SQL statement (the body of the START AFTER COMMIT statement) is executed asynchronously in a separate connection.
For example, if you want to call a stored procedure that is named my_proc when a transaction is committed, you write:
START AFTER COMMIT NONUNIQUE CALL
  my_proc;
The following example shows the use of a START AFTER COMMIT statement inside a transaction.
-- Any valid SQL statement(s)...
...
-- Creation phase. The function my_proc() is not actually called here.
START AFTER COMMIT NONUNIQUE CALL my_proc(x, y);
...
-- Any valid SQL statement(s)...
-- Execution phase: This ends the transaction and starts execution
-- of the call to my_proc().
COMMIT WORK;
A common use of the START AFTER COMMIT statement is to implement the Sync Pull Notify functionality (push synchronization) in Advanced Replication setups, see Sync Pull Notify.
Before executing each statement on a replica, a connection to the replica is established.
When the START AFTER COMMIT statement is used to call multiple replicas, you can use the optional keyword DEFAULT in the syntax of the CALL statement. For example, suppose that you use the following statement:
START AFTER COMMIT FOR EACH REPLICA WHERE location = 'India' UNIQUE CALL push;
Then in the local procedure "push", you can use the keyword DEFAULT, which acts as a variable that contains the name of the replica in question.
CREATE PROCEDURE push
BEGIN
  EXEC SQL EXECDIRECT CALL remoteproc AT DEFAULT;
END
The procedure push is called one time for each replica that has a property named location with the value India. Each time the procedure is called, DEFAULT is set to the name of that replica. Thus, the following statement calls the procedure on that particular replica:
CALL remoteproc AT DEFAULT;
You can set the replica properties in the master with the SET SYNC PROPERTY statement, see SET SYNC PROPERTY:
For example:
SET SYNC PROPERTY location = 'India' FOR REPLICA asia_hq;
The statement specified in the START AFTER COMMIT statement is executed as an independent transaction. It is not part of the transaction that contained the START AFTER COMMIT statement. This independent transaction is run as though autocommit mode is on; in other words, you do not need an explicit COMMIT WORK to commit the work done in this statement.
However, the execution of the statement is not much like a transaction for the following reasons:
There is no guarantee that the statement will execute to completion. The statement is launched as an independent background task. If the server crashes, or if for some other reason the statement cannot be executed, then the statement disappears without being completely executed.
Because the statement is executed as a background task, there is no mechanism for returning an error.
There is no way to roll back the statement; if the statement execution is completed, the statement is autocommitted regardless of whether any errors were detected. If the statement is a procedure call, then the procedure itself might contain COMMIT and ROLLBACK commands.
If the statement fails, you can use the RETRY clause to try executing the statement again. The RETRY clause allows you to specify the number of times the server should try the failed statement again. You must specify the number of seconds to wait between each attempt.
If you do not use the RETRY clause, the server attempts to execute the statement only one time and then the statement is discarded. For example, if the statement tries to call a remote procedure and the remote server is down (or cannot be contacted due to a network problem), then the statement is not executed and you do not get any error message.
Any statement, including the statement that is specified in a START AFTER COMMIT statement, executes in a certain context. The context includes such factors as the default catalog, the default schema, and so on. For a statement that is executed from within a START AFTER COMMIT statement, the statement context is based on the context at the time that the START AFTER COMMIT statement is executed, not on the context at the time of the COMMIT WORK that actually causes the statement inside the START AFTER COMMIT statement to run.
In the following example, CALL FOO_PROC is executed in the catalog foo_cat and schema foo_schema, not bar_cat and bar_schema.
SET CATALOG foo_cat;
SET SCHEMA foo_schema;
START AFTER COMMIT UNIQUE CALL foo_proc;
...
SET CATALOG bar_cat;
SET SCHEMA bar_schema;
COMMIT WORK;
The UNIQUE and NONUNIQUE keywords determine whether the server tries to avoid issuing the same statement twice.
The UNIQUE keyword before a statement defines that the statement is executed only if there is no identical statement being executed or pending for execution. Statements are compared with a simple string compare. So for example CALL foo(1) is different from CALL foo(2). Replicas are also taken into account in the comparison; in other words, the UNIQUE keyword does not prevent the server from executing the same trigger call on different replica databases. Note that the UNIQUE keyword only blocks the execution of overlapping statements; it does not prevent the same statement from being executed again if it is called after the current execution has completed.
NONUNIQUE means that duplicate statements can be executed simultaneously in the background.
For example, the following statements are all considered different and are thus executed even though each contains the UNIQUE keyword (name is a unique property in each replica database).
START AFTER COMMIT UNIQUE call myproc;
START AFTER COMMIT FOR EACH REPLICA WHERE name='R1' UNIQUE call myproc;
START AFTER COMMIT FOR EACH REPLICA WHERE name='R2' UNIQUE call myproc;
START AFTER COMMIT FOR EACH REPLICA WHERE name='R3' UNIQUE call myproc;
But if the following statement is executed in the same transaction as the previous ones and any of the databases, where name='R1', 'R2', or 'R3', also have the property color='blue', then the call is not executed for those replicas again.
START AFTER COMMIT FOR EACH REPLICA WHERE color='blue' UNIQUE call myproc;
Uniqueness applies only to statements that are started by a START AFTER COMMIT statement. For example, if you manually execute a statement that refreshes from a particular publication, and the master database also calls a remote stored procedure to refresh from that publication, the master database does not skip the call because the manual refresh is already running.
The START AFTER COMMIT statement can be used inside a stored procedure. For example, suppose that you want to post an event if, and only if, a transaction completed successfully. You could write a stored procedure that would execute a START AFTER COMMIT statement that would post the event if the transaction was committed (but not if it was rolled back). Your code might look similar to the following example:
Note This sample also contains an example of receiving and then using an event parameter. See the stored procedure named wait_on_event_e in SCRIPT #1.
-- To run this demo properly, you need two users/connections.
-- This demo contains 5 separate "scripts", which must be executed
-- in the following order:
-- User1 executes SCRIPT #1.
-- User2 executes SCRIPT #2.
-- User1 executes SCRIPT #3.
-- User2 executes SCRIPT #4.
-- User1 executes SCRIPT #5.
-- You might notice that there are some COMMIT WORK statements
-- in surprising places. These are to ensure that each user sees the
-- most recent changes of the other user. Without the COMMIT WORK
-- statements, in some cases one user would see an out-of-date
-- snapshot of the database.
--
-- Set autocommit off for both users/connections!

-- SCRIPT #1 (USER 1)
CREATE EVENT e (i int); CREATE TABLE table1 (a int);
-- This inserts a row into table1. The value inserted into the is copied
-- from the parameter to the procedure.
"CREATE PROCEDURE inserter(i integer)
BEGIN
  EXEC SQL PREPARE c_inserter INSERT INTO table1 (a) VALUES (?);
  EXEC SQL EXECUTE c_inserter USING (i);
  EXEC SQL CLOSE c_inserter;
  EXEC SQL DROP c_inserter;
END";
-- This posts the event named "e".
"CREATE PROCEDURE post_event(i integer)
BEGIN
  POST EVENT e(i);
END";
-- This demonstrates the use of START AFTER COMMIT inside a
-- stored procedure. After you call this procedure and
-- call COMMIT WORK, the server will post the event.
"CREATE PROCEDURE sac_demo
BEGIN
  DECLARE MyVar INT;
  MyVar := 97;
  EXEC SQL PREPARE c_sacdemo START AFTER COMMIT NONUNIQUE CALL
    post_event(?);
  EXEC SQL EXECUTE c_sacdemo USING (MyVar);
  EXEC SQL CLOSE c_sacdemo; EXEC SQL DROP c_sacdemo;
END";
-- When user2 calls this procedure, the procedure will wait until
-- the event named "e" is posted, and then it will call the
-- stored procedure that inserts a record into table1.
"CREATE PROCEDURE wait_on_event_e
BEGIN
-- Declare the variable that will be used to hold the event parameter.
-- Although the parameter was declared when the event was created, you
-- still need to declare it as a variable in the procedure that receives
-- that event.
DECLARE i INT;
WAIT EVENT
WHEN e (i) BEGIN
-- After we receive the event, insert a row into the table.
EXEC SQL PREPARE c_call_inserter CALL inserter(?);
EXEC SQL EXECUTE c_call_inserter USING (i);
EXEC SQL CLOSE c_call_inserter;
EXEC SQL DROP c_call_inserter;
END EVENT
END WAIT
END";
COMMIT WORK;

-- SCRIPT #2 (USER 2)
-- Make sure that User2 sees the changes that User1 made.
COMMIT WORK;
-- Wait until user1 posts the event.
CALL wait_on_event_e;
-- Do not commit work again (yet).

-- SCRIPT #3 (USER 1)
COMMIT WORK;
-- User2 should be waiting on event e, and should see the event after
-- we execute the stored procedure named sac_demo and then commit work.
-- Note that since START AFTER COMMIT statements are executed
-- asynchronously, there might be a slight delay between the COMMIT WORK
-- and the associated POST EVENT.
CALL sac_demo;
COMMIT WORK;

-- SCRIPT #4 (USER 2)
-- Commit the INSERT that User1 did when they called inserter() in SCRIPT #1
-- after receiving the event
COMMIT WORK;

-- SCRIPT #5 (USER 1)
-- Ensure that User1 sees the data that User2 inserted.
COMMIT WORK;
-- Show the record that User2 inserted.
SELECT * FROM table1;
COMMIT WORK;
Go up to
Remote stored procedures in Advanced Replication configurations