zl程序教程

您现在的位置是:首页 >  工具

当前栏目

第二十六章 Caché 命令大全 TROLLBACK 命令

命令 大全 Cach
2023-09-11 14:15:37 时间

第二十六章 Caché 命令大全 TROLLBACK 命令

回滚不成功的事务。

重点

  1. 默认情况下,在进行事务时,其他进程会立即看到全局变量的SETKILL。为防止在提交事务之前其他用户看到在事务中调用的全局变量的SETKILL,必须通过LOCK命令协调对全局变量的访问。

大纲

TROLLBACK:pc
TRO:pc

TROLLBACK:pc 1
TRO:pc 1

参数

  • pc - 可选-后置表达式
  • 1 -可选 - 整数1。回滚一级嵌套。必须指定为文字。

描述

TROLLBACK终止当前事务,并将所有日志记录的数据库值还原为它们在事务开始时保留的值。回滚有两种形式:

  • TROLLBACK回滚正在进行的所有事务(无论发出了多少级别的TSTART),并将$TLEVEL重置为0。
  • TROLLBACK 1回滚嵌套事务的当前级别(由最近的TSTART启动的事务)并将$TLEVEL递减1。1参数必须是文字数字1;它不能是解析为1的变量或表达式。不支持1以外的数字。

可以从$TLEVEL特殊变量确定嵌套事务的级别。当$TLEVEL为0时调用TRolback没有任何效果。

可以使用%SYS.Journal.System类的GetImageJournalInfo()方法在日志文件中搜索TSTART命令,从而识别打开的事务。TSTART递增$TLEVEL并写入日志文件记录:如果$TLEVEL为零,则为“BT”(BEGIN TRANSACTION)记录,如果$TLEVEL大于0,则为“BTL”(BEGIN TRANSACTION WITH LEVEL)记录。成功执行回滚操作后,使用%SYS.Journal.System类的Sync()方法刷新日志缓冲区。

回滚在回滚操作期间禁用Ctrl-C中断。

什么是回滚,什么不是回滚

回滚将回滚所有记录的操作。这些包括:

TROLLBACK将大多数更改回滚到全局变量,包括SETKILL操作。全局变量是许多Caché运算的基础。回滚操作不会还原局部变量。 TROLLBACK在事务期间回滚对全局变量中的位字符串值所做的更改。但是,回滚操作不会将全局变量位字符串返回其先前的内部字符串表示形式。

TROLLBACK回滚插入,更新和删除对SQL数据的更改。

但是,并非对应用程序所做的所有更改都记录在日志中:

  • TROLLBACK不会将更改回滚到局部变量或私有进程全局变量。
  • TROLLBACK不会回滚对特殊变量(例如$TEST)的更改。
  • TROLLBACK不会回滚对当前名称空间所做的更改。
  • TROLLBACK不会回滚LOCK命令的锁定或解锁操作。
  • TROLLBACK不会将$INCREMENT(或$ZINCREMENT)更改回滚到全局变量。
  • TROLLBACK不会将$SEQUENCE更改回滚到全局变量。

Caché将全局变量的SETKILL视为日记交易事件;回滚事务会逆转这些操作。 Caché不会将局部变量或进程专用全局变量的SETKILL视为日记交易事件;回滚交易对这些操作没有影响。

默认情况下,在进行事务时,其他进程会立即看到全局变量的SETKILL。为防止在提交事务之前其他用户看到在事务中调用的全局变量的SET``或KILL,必须通过LOCK`命令协调对全局变量的访问。

事务回滚日志

如果在回滚操作期间发生错误,则Caché会发出错误消息,并将错误消息记录在cconsole.log操作员控制台日志文件中。可以使用管理门户网站系统操作选项来查看cconsole.log:[主页]> [系统日志]> [查看控制台日志]。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KEKk71b-1595720610325)(02638064246B46118C98A0AC68B8B9AC)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPyYlNkI-1595720610328)(9396D1265B8246AF9225AEC665A68F17)]

默认情况下,操作员控制台日志文件是Caché系统管理目录(Mgr)中的cconsole.log。此默认值是可配置的。转到管理门户网站系统管理选项,依次选择配置,其他设置和高级内存([主页]> [配置]> [高级内存设置])。查看和编辑ConsoleFile的当前设置。默认情况下,此设置为空,将控制台消息路由到MGR目录中的cconsole.log。如果更改此设置,则必须重新启动Caché,此更改才能生效。

错误

如果TROLLBACK无法成功回滚事务,则会出现错误。处理行为取决于系统范围的日记帐配置设置标志“Freeze On Error”的设置([主页]> [配置]> [Jounal设置]):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlsifUkN-1595720610329)(2C744E5450234044B61501AC0C9A180F)]

  • 如果未设置Freeze On Error(缺省设置),则该进程将收到<ROLLFAIL>错误。该事务被关闭,并且为该事务保留的任何锁都被释放。此选项以数据完整性换取系统可用性。

  • 如果设置了Freeze On Error,则进程会暂停,并且清理作业守护进程(CLNDMN)会重试回滚打开的事务。在CLNDMN重试期间,为事务保留的锁是完好无损的,因此,系统可能会挂起。此选项以系统可用性换取数据完整性。

<ROLLFAIL>发生时,Caché%msg既记录<ROLLFAIL>错误本身,也记录导致回滚的前一个错误。例如,尝试使用超出范围的值更新日期,然后回滚失败可能会返回以下%msg

SQLCODE = -105 %msg = Unexpected error occurred: <ROLLFAIL>%0Ac+1^dpv during TROLLBACK. Previous error: SQLCODE=-105, %msg='Field 'Sample.Person.DOB' (value '5888326') failed validation'.

如果事务内的全局数据库访问远程数据库,然后程序显式卸载该远程数据库,则在事务回滚时会发生<ROLLFAIL>

如果进程在更改数据库之前禁用日志记录,并且调用事务回滚时出现错误,则在事务回滚时会发生<ROLLFAIL>。如果进程在所有数据库更改完成之后发出TRolback命令之前禁用日志记录,则在事务回滚时不会发生<ROLLFAIL>。相反,Caché会在回滚操作期间临时启用日志记录。回滚操作完成后,Caché将再次禁用日志记录

SQL事务

CachéObjectScript和SQL TRANSACTION命令完全兼容且可互换,但有以下例外:

如果没有当前事务,ObjectScript TSTARTSQL START TRANSACTION都会启动事务。但是,START TRANSACTION不支持嵌套事务。因此,如果需要(或可能需要)嵌套事务,最好使用TSTART启动事务。如果需要与SQL标准兼容,请使用START TRANSACTION

CachéObjectScript事务处理为嵌套事务提供有限的支持。SQL事务处理为事务内的保存点提供支持。

清除缓存查询

如果在事务期间调用%SYSTEM.SQL类的Purge()方法来清除缓存的查询,则缓存的查询将被永久删除。后续的TROLLBACK将不会还原已清除的缓存查询。

Globals 与 TROLLBACK 1

TROLLBACK 1回滚并恢复其嵌套事务中更改的所有全局变量。但是,如果将全局变量更改为映射到不支持嵌套事务的远程系统,则这些更改将视为发生在最外层的嵌套级别。仅当回滚将$ LEVEL重置为0时(通过调用TROLLBACK或在$ T LEVEL = 1时调用TROLLBACK 1时),才会此类全局变量回滚。

Locks 与 TROLLBACK 1

TROLLBACK 1不会将在其嵌套事务期间建立的锁恢复到其先前状态。在事务期间建立的所有锁都保留在锁表中,直到通过TROLLBACK到级别0或TCOMMIT结束事务为止。那时,Caché释放在嵌套事务期间创建的所有锁,并将所有先前存在的锁恢复到TSTART之前的状态。嵌套事务的TCOMMIT不会释放相应的锁,因此后续的TROLLBACK可以在已提交的子事务中实现锁。

参数

pc

可选的后置条件表达式。如果后置条件表达式为true(计算为非零数值),则Caché执行命令。如果后置条件表达式为假(计算为零),则Caché不执行该命令。

示例

下面的示例使用单级事务将随机金额的资金从一个帐户转移到另一个帐户。如果转帐金额大于可用余额,程序将使用回退来回退事务处理:

/// d ##class(PHA.TEST.Command).TestBankAccounts()
ClassMethod TestBankAccounts()
{
SetupBankAccounts
	SET num=12345
	SET ^CHECKING(num,"balance")=500.99
	SET ^SAVINGS(num,"balance")=100.22
	IF $DATA(^NumberOfTransfers)=0 {
		SET ^NumberOfTransfers=0
	}
BankTransfer
	WRITE "转移之前:",!,"Checking=$",^CHECKING(num,"balance")," Savings=$",^SAVINGS(num,"balance"),!
	// 将资金从一个账户转到另一个账户
	SET transfer=$RANDOM(1000)
	WRITE "转账金额 $",transfer,!
	DO CkToSav(num,transfer)
	IF ok=1 {
		WRITE "成功转移",!,"迄今为止的转账次数=",^NumberOfTransfers,!
	} ELSE {
		WRITE "*** 资金不足 ***",!
	}
	WRITE "转移之后:",!,"Checking=$",^CHECKING(num,"balance")," Savings=$",^SAVINGS(num,"balance"),!
	RETURN
CkToSav(acct,amt)
	TSTART
	SET ^CHECKING(acct,"balance") = ^CHECKING(acct,"balance") - amt
	SET ^SAVINGS(acct,"balance") = ^SAVINGS(acct,"balance") + amt
	SET ^NumberOfTransfers=^NumberOfTransfers + 1
	IF ^CHECKING(acct,"balance") > 0 {
		TCOMMIT  SET ok=1 QUIT:ok
	}
	ELSE {
		TROLLBACK  SET ok=0 QUIT:ok
	}
}

DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $222
成功转移
迄今为止的转账次数=1
转移之后:
Checking=$278.99 Savings=$322.22
 
DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $5
成功转移
迄今为止的转账次数=2
转移之后:
Checking=$495.99 Savings=$105.22
 
DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $893
*** 资金不足 ***
转移之后:
Checking=$500.99 Savings=$100.22
 
DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $62
成功转移
迄今为止的转账次数=3
转移之后:
Checking=$438.99 Savings=$162.22
 
DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $221
成功转移
迄今为止的转账次数=4
转移之后:
Checking=$279.99 Savings=$321.22
 
DHC-APP>d ##class(PHA.TEST.Command).TestBankAccounts()
转移之前:
Checking=$500.99 Savings=$100.22
转账金额 $545
*** 资金不足 ***
转移之后:
Checking=$500.99 Savings=$100.22

以下示例显示回滚对嵌套事务的影响。每个TSTART递增$TLEVEL并设置全局变量。在内部嵌套事务上发出TCOMMIT会使$TLEVEL递减,但在嵌套事务中所做更改的提交会延迟。在这种情况下,外部事务的后续回滚将回滚所做的所有更改,包括内部“已提交”嵌套事务中的更改。

/// d ##class(PHA.TEST.Command).TestTro()
ClassMethod TestTro()
{
	SET ^a(1)="[- - -]",^b(1)="[- - -]"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TSTART
		LOCK +^a(1)
		SET ^a(1)="hello"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TSTART
			LOCK +^b(1)
			SET ^b(1)="world"
			WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TCOMMIT
		WRITE !,"After TCOMMIT"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TROLLBACK
	WRITE !,"After TROLLBACK"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
	QUIT
}
DHC-APP>d ##class(PHA.TEST.Command).TestTro()
 
level:0 [- - -] [- - -]
level:1 hello [- - -]
level:2 hello world
After TCOMMIT
level:1 hello world
After TROLLBACK
level:0 [- - -] [- - -]
/// d ##class(PHA.TEST.Command).TestTro()
ClassMethod TestTro()
{
	SET ^a(1)="[- - -]",^b(1)="[- - -]"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TSTART
		LOCK +^a(1)
		SET ^a(1)="hello"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TSTART
			LOCK +^b(1)
			SET ^b(1)="world"
			WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TRO
		WRITE !,"After TCOMMIT"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TROLLBACK
	WRITE !,"After TROLLBACK"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
	QUIT
}
DHC-APP>d ##class(PHA.TEST.Command).TestTro()
 
level:0 [- - -] [- - -]
level:1 hello [- - -]
level:2 hello world
After TCOMMIT
level:0 [- - -] [- - -]
After TROLLBACK
level:0 [- - -] [- - -]
/// d ##class(PHA.TEST.Command).TestTro()
ClassMethod TestTro()
{
	SET ^a(1)="[- - -]",^b(1)="[- - -]"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TSTART
		LOCK +^a(1)
		SET ^a(1)="hello"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TSTART
			LOCK +^b(1)
			SET ^b(1)="world"
			WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
			TRO 1
		WRITE !,"After TCOMMIT"
		WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
		TROLLBACK
	WRITE !,"After TROLLBACK"
	WRITE !,"level:",$TLEVEL," ",^a(1)," ",^b(1)
	QUIT
}
DHC-APP>d ##class(PHA.TEST.Command).TestTro()
 
level:0 [- - -] [- - -]
level:1 hello [- - -]
level:2 hello world
After TCOMMIT
level:1 hello [- - -]
After TROLLBACK
level:0 [- - -] [- - -]

下面的示例显示Troll Back如何回滚全局变量,而不是局部变量:

/// d ##class(PHA.TEST.Command).TestTro1()
ClassMethod TestTro1()
{
	SET x="default",^y="default"
	WRITE !,"level:",$TLEVEL
	WRITE !,"local:",x," global:",^y
		TSTART
		SET x="first",^y="first"
		WRITE !,"TSTART level:",$TLEVEL
		WRITE !,"local:",x," global:",^y
			TSTART
			SET x=x_" second",^y=^y_" second"
			WRITE !,"TSTART level:",$TLEVEL
			WRITE !,"local:",x," global:",^y
				TSTART
				SET x=x_" third",^y=^y_" third"
				WRITE !,"TSTART level:",$TLEVEL
				WRITE !,"local:",x," global:",^y
		TROLLBACK
	WRITE !!,"After Rollback:"
	WRITE !,"TROLLBACK level:",$TLEVEL
	WRITE !,"local:",x," global:",^y
}
DHC-APP>d ##class(PHA.TEST.Command).TestTro1()
 
level:0
local:default global:default
TSTART level:1
local:first global:first
TSTART level:2
local:first second global:first second
TSTART level:3
local:first second third global:first second third
 
After Rollback:
TROLLBACK level:0
local:first second third global:default

下面的示例显示如何不回滚对全局的$INCREMENT更改。

/// d ##class(PHA.TEST.Command).TestTroINCREMENT()
ClassMethod TestTroINCREMENT()
{
	SET ^x=-1,^y=0,^z=""
	WRITE !,"level:",$TLEVEL
	WRITE !,"Increment:",$INCREMENT(^x)," Add:",^y
		TSTART
		SET ^y=^y+1
		WRITE !,"level:",$TLEVEL
		WRITE !,"Increment:",$INCREMENT(^x)," Add:",^y
			TSTART
			SET ^y=^y+1,^z=^z_" second"
			WRITE !,"level:",$TLEVEL
			WRITE !,"Increment:",$INCREMENT(^x)," Add:",^y
				TSTART
				SET ^y=^y+1,^z=^z_" third"
				WRITE !,"level:",$TLEVEL
				WRITE !,"Increment:",$INCREMENT(^x)," Add:",^y
		TROLLBACK
	WRITE !!,"After Rollback"
	WRITE !,"level:",$TLEVEL
	WRITE !,"Increment:",^x," Add:",^y
}
DHC-APP>d ##class(PHA.TEST.Command).TestTroINCREMENT()
 
level:0
Increment:0 Add:0
level:1
Increment:1 Add:1
level:2
Increment:2 Add:2
level:3
Increment:3 Add:3
 
After Rollback
level:0
Increment:3 Add:0