#!/usr/bin/php 0 !\n"); if ($nbSession > 100) die("Number of sessions (".$options['s'].") must be <= 100 !\n"); break; case 'v': $verbose = true; break; } // check the group name has been supplied if ($groups == ''){ die("At least one group name must be supplied with the -g parameter !\n"); } // check the mark has been supplied if ($mark == ''){ die("A mark must be supplied with the -m parameter !\n"); } // Open all required sessions // There is 1 session per corridor, the first being also used for global dialog with pg // They all use the same connection parameters. // Connection parameters are optional. If not supplied, the environment variables and PostgreSQL default values are used for ($i = 1 ; $i <= $nbSession ; $i++){ if ($verbose) echo date("d/m/Y - H:i:s.u")." Opening session #$i...\n"; $dbconn[$i] = pg_connect($conn_string,PGSQL_CONNECT_FORCE_NEW) or die("Connection #$i failed".pg_last_error()."\n"); } // For each session, start a transaction for ($i = 1 ; $i <= $nbSession ; $i++){ if ($verbose) echo date("d/m/Y - H:i:s.u")." Start transaction #$i...\n"; $result = pg_query($dbconn[$i],"BEGIN TRANSACTION") or die('Begin transaction #'.$i.' failed: '.pg_last_error()."\n"); } pg_free_result($result); // Call _rlbk_init() on first session // This checks the groups and mark, and prepares the parallel rollback by creating well balanced sessions $query = "SELECT emaj._rlbk_init (array[".$groups."],'".pg_escape_string($mark)."',$isLogged,$nbSession, $multiGroup)"; if ($verbose) echo date("d/m/Y - H:i:s.u")." _rlbk_init for groups $groups and mark $mark...\n"; $result = pg_query($dbconn[1],$query) or die('Call of _rlbk_init() function failed '.pg_last_error()."\n"); $rlbkId = pg_fetch_result($result,0,0); pg_free_result($result); echo "==> $msgRlbk to mark '$mark' is now in progress with $nbSession sessions...\n"; // For each session, synchronously call _rlbk_session_lock() to lock all tables for ($i = 1 ; $i <= $nbSession ; $i++){ $query = "SELECT emaj._rlbk_session_lock ($rlbkId,$i)"; if ($verbose) echo date("d/m/Y - H:i:s.u")." _rlbk_session_lock for session #$i -> lock tables...\n"; $result = pg_query($dbconn[$i],$query) or die('Call of _rlbk_session_lock() function for #'.$i.' failed: '.pg_last_error()."\n"); } pg_free_result($result); // Call _rlbk_start_mark() on first session // This sets a rollback start mark if logged rollback $query = "SELECT emaj._rlbk_start_mark ($rlbkId,$multiGroup)"; if ($verbose) echo date("d/m/Y - H:i:s.u")." _rlbk_start_mark...\n"; $result = pg_query($dbconn[1],$query) or die('Call of _rlbk_start_mark() function failed '.pg_last_error()."\n"); pg_free_result($result); // For each session, asynchronously call _rlbk_exec() to start the planned steps execution for ($i = 1 ; $i <= $nbSession ; $i++){ $query = "SELECT emaj._rlbk_session_exec ($rlbkId,$i)"; if (pg_connection_busy($dbconn[$i])) die("Session #$i is busy. Unable to call for _rlbk_groups_step5\n"); if ($verbose) echo date("d/m/Y - H:i:s.u")." _rlbk_session_exec for session #$i -> rollback tables...\n"; pg_send_query($dbconn[$i],$query) or die('Call of _rlbk_session_exec() function for #'.$i.' failed: '.pg_last_error()."\n"); } // For each session, get the result of the previous call of _rlbk_exec() $cumNbTbl = 0; for ($i = 1 ; $i <= $nbSession ; $i++){ $result = pg_get_result($dbconn[$i]) or die('Getting the result of the _rlbk_session_exec() function failed '.pg_last_error()."\n"); if ($verbose) echo date("d/m/Y - H:i:s.u")." get result of _rlbk_session_exec call for session #$i...\n"; if (pg_result_error($result)) die("Execution of _rlbk_session_exec function failed \n".pg_result_error($result)."\n"); $nbTbl = pg_fetch_result($result,0,0); if ($verbose) echo "===> Number of rollbacked tables for session $i = $nbTbl\n"; $cumNbTbl = $cumNbTbl + $nbTbl; } pg_free_result($result); // Call emaj_rlbk_end() on first session to complete the rollback operation $query = "SELECT emaj._rlbk_end ($rlbkId,$multiGroup)"; if ($verbose) echo date("d/m/Y - H:i:s.u")." _rlbk_end -> complete rollback operation...\n"; $result = pg_query($dbconn[1],$query) or die('Call of _rlbk_end() function failed '.pg_last_error()."\n"); $nbSeq = pg_fetch_result($result,0,0); pg_free_result($result); if ($verbose) echo "===> Number of rollbacked sequences for groups '$groups' = $nbSeq\n"; // If there is only 1 session, perform a usual COMMIT if ($nbSession == 1){ if ($verbose) echo date("d/m/Y - H:i:s.u")." Commit transaction #1...\n"; $result = pg_query($dbconn[1],"COMMIT") or die('Commit prepared #1 failed: '.pg_last_error()."\n"); }else{ // else, COMMIT with 2PC to be sure that all sessions can either commit or rollback in a single transaction // Phase 1 : Prepare transaction for ($i = 1 ; $i <= $nbSession ; $i++){ if ($verbose) echo date("d/m/Y - H:i:s.u")." Prepare transaction #$i...\n"; $result = pg_query($dbconn[$i],"PREPARE TRANSACTION 'emajtx".$i."'") or die('Prepare transaction #'.$i.' failed: '.pg_last_error()."\n"); } // Phase 2 : Commit for ($i = 1 ; $i <= $nbSession ; $i++){ if ($verbose) echo date("d/m/Y - H:i:s.u")." Commit transaction #$i...\n"; $result = pg_query($dbconn[$i],"COMMIT PREPARED 'emajtx".$i."'") or die('Commit prepared #'.$i.' failed: '.pg_last_error()."\n"); } } // Call the emaj_cleanup_rollback_state() function to set the rollback event as committed $query = "SELECT emaj.emaj_cleanup_rollback_state()"; if ($verbose) echo date("d/m/Y - H:i:s.u")." emaj_cleanup_rollback_state -> set the rollback event as committed...\n"; $result = pg_query($dbconn[1],$query) or die('Call of emaj_cleanup_rollback_state() function failed '.pg_last_error()."\n"); pg_free_result($result); // Close the sessions if ($verbose) echo date("d/m/Y - H:i:s.u")." Closing all sessions...\n"; for ($i = 1 ; $i <= $nbSession ; $i++){ pg_close($dbconn[$i]); } // And issue the final message echo "==> $msgRlbk completed ($cumNbTbl tables and $nbSeq sequences effectively processed).\n"; function print_help(){ global $progName,$EmajVersion; echo "$progName belongs to the E-Maj PostgreSQL extension (version $EmajVersion).\n"; echo "It performs E-Maj rollback for one or several groups and a previously set mark, processing tables in parallel.\n\n"; echo "Usage:\n"; echo " $progName -g -m -s [OPTION]... \n"; echo "\nOptions:\n"; echo " -l logged rollback mode (i.e. 'rollbackable' rollback)\n"; echo " -v verbose mode; writes more information about the processing\n"; echo " --help shows this help, then exit\n"; echo " --version outputs version information, then exit\n"; echo "\nConnection options:\n"; echo " -d, database to connect to\n"; echo " -h, database server host or socket directory\n"; echo " -p, database server port\n"; echo " -U, user name to connect as\n"; echo " -W, password associated to the user, if needed\n"; echo "\nExamples:\n"; echo " php emajParallelRollback.php -g myGroup1 -m myMark -s 3 \n"; echo " performs a parallel rollback of table group myGroup1 to mark\n"; echo " myMark using 3 parallel sessions.\n"; echo " php emajParallelRollback.php -h localhost -p 5432 -d myDb -U emajadm -l -g \"myGroup1,myGroup2\" -m myMark -s 5 -v\n"; echo " lets the role emajadm perform a parallel logged rollback of 2 table\n"; echo " groups to mark myMark using 5 parallel sessions, in verbose mode.\n"; } function print_version(){ global $progName,$EmajVersion; echo "This version of $progName belongs to E-Maj version $EmajVersion.\n"; echo "Type '$progName --help' to get usage information\n\n"; } ?>