- Log in to post comments
Include EnsConstants
/// Get Settings from Production and Create DefaultSettings
Class CONNECTORSPKG.Utility.DefaultSettings
{
/// List productions
ClassMethod ListProductions(print As %Boolean = 1, namespace As %String = {$NAMESPACE}) As %String
{
new $NAMESPACE
set $NAMESPACE = namespace
set production = ""
if print
{
write "Productions in namespace " _ namespace _ ":",!
}
set prdRS = ##class(%ResultSet).%New("Ens.Config.Production:ProductionStatus")
do prdRS.Execute()
while (prdRS.Next())
{
set status = prdRS.Data("Status")
if status = "Running"
{
set production = prdRS.Data("Production")
}
if print
{
write prdRS.Data("Production")," (",status,")",!
}
}
return production
}
/// Export to settingsfile
ClassMethod Export() As %Status
{
return ##class(Ens.Config.DefaultSettings).%Export("/home/irisowner/systemdefaults/Ens.Config.DefaultSettings.esd", "MONDRIAAN.FoundationProduction")
}
/// Export from settingsfile
ClassMethod Import() As %Status
{
set sc = ##class(Ens.Config.DefaultSettings).%Import("/home/irisowner/systemdefaults/Ens.Config.DefaultSettings.esd", .count, .idsimported)
write "Imported ",count," settings",!
return sc
}
/// Connectors action
ClassMethod SettingsFromProduction(removeFromProduction As %Boolean = 0, updateSettings As %Boolean = 1) As %Status
{
return ..GetSettingsFromProduction("CONNECTORSPKG.FoundationProduction", "*:HTTPServer,SSLConfig;CONNECTORSPKG.BO.GenericHTTP.Operation:HttpMethod,AcceptHeader,ContentType,URL,AuthorizationType,Credentials,CustomAuthorizationHeader,AlertOnError,ReplyCodeActions", removeFromProduction, updateSettings)
}
/// Get settings From Production
ClassMethod GetSettingsFromProduction(production As %String = {..ListProductions(0, $NAMESPACE)}, filter As %String = "", removeFromProduction As %Boolean = 0, updateSettings As %Boolean = 1) As %Status
{
write "Settings in production '",production _ "':",!
set xdataId = production _ "||ProductionDefinition"
set xdata = ##class(%Dictionary.XDataDefinition).%OpenId(xdataId, , .sc)
if $$$ISERR(sc)
{
write "Failed to open XData block '" _ xdataId,"': ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
set tProduction = ##class(Ens.Config.Production).%OpenId(production,,.sc)
if $$$ISERR(sc)
{
write "Failed to open production '",production,"': ",$SYSTEM.Status.GetErrorText(sc),"; settings will not be removed from the production",!
set removeFromProduction = 0
}
set sc = ##class(%XML.TextReader).ParseStream(xdata.Data, .textreader)
#dim textreader as %XML.TextReader
if $$$ISERR(sc)
{
write "Failed to parse XData block '" _xdataId,"': ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
set currentItemName = ""
set currentItemClass = ""
#dim tRemovedItemCount as %Integer = 0
while textreader.Read()
{
if (textreader.NodeType '= "element")
{
continue
}
if (textreader.LocalName = "Item")
{
if (textreader.MoveToAttributeName("Name"))
{
set currentItemName = textreader.Value
}
if (textreader.MoveToAttributeName("ClassName"))
{
set currentItemClass = textreader.Value
}
#; write "Found Item with Name='" _ currentItemName _ "' of class '" _ currentItemClass _ "':",!
continue
}
if (textreader.LocalName = "Setting")
{
do ..HandleXDataSetting(textreader, tProduction, currentItemName, currentItemClass, filter, removeFromProduction, updateSettings, .tRemovedItemCount)
}
}
if tRemovedItemCount > 0
{
write "Removed ",tRemovedItemCount," settings; now saving Production '",production,"':"
return ..SaveProduction(tProduction)
}
return $$$OK
}
/// Hanlde setting found in the XData
ClassMethod HandleXDataSetting(textreader As %XML.TextReader, tProduction As Ens.Config.Production, currentItemName As %String, currentItemClass As %String, filter As %String, removeFromProduction As %Boolean, updateSettings As %Boolean, ByRef tRemovedItemCount As %Integer)
{
if (textreader.MoveToAttributeName("Target"))
{
set target = textreader.Value
}
if (textreader.MoveToAttributeName("Name"))
{
set name = textreader.Value
}
do textreader.Read() // Read value
set value = textreader.Value
#; write "Found Setting Target=",target,", name=",name,", Value=",value,!
if ..InFilter(filter, currentItemName, currentItemClass, target, name, value)
{
if updateSettings && $$$ISERR(..HandleSetting(tProduction.Name, currentItemName, currentItemClass, target, name, value))
{
return
}
if removeFromProduction && ..RemoveSettingFromProduction(tProduction, currentItemName, target, name)
{
set tRemovedItemCount = tRemovedItemCount + 1
}
}
}
/// SaveProduction
ClassMethod SaveProduction(tProduction As Ens.Config.Production) As %Status
{
// Save the changes we made to the production
set sc = tProduction.%Save(1)
if $$$ISERR(sc)
{
write " failed: ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
write !
// Regenerate the XData in the corresponding class
write "Save Production XData: "
Set sc = tProduction.SaveToClass()
if $$$ISERR(sc)
{
write " failed: ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
write !
// Grab the state of the production
set sc = ##class(Ens.Director).GetProductionStatus(.tRunningProduction, .tState)
if $$$ISERR(sc)
{
write "Failed to get Production status: ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
// Finally, does the production need updating?
if (tRunningProduction = tProduction.Name) && (tState = $$$eProductionStateRunning)
{
// Update the running production with the new settings
write "Update running production: "
set sc = ##class(Ens.Director).UpdateProduction(##class(Ens.Director).GetRunningProductionUpdateTimeout())
if $$$ISERR(sc)
{
write " failed: ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
}
return $$$OK
}
/// Determine if the setting is in the Filter
ClassMethod InFilter(filter As %String, itemName As %String, class As %String, target As %String, varName As %String, value As %String) As %Boolean
{
if filter = ""
{
return 1
}
for classIndex = 1:1:$LENGTH(filter, ";")
{
set classPart = $PIECE(filter, ";", classIndex)
set configClass = $PIECE(classPart, ":", 1)
if (configClass '= "*") && (configClass '= class)
{
continue
}
set vars = $PIECE(classPart, ":", 2)
for varIndex = 1:1:$LENGTH(vars, ",")
{
if $PIECE(vars, ",", varIndex) = varName
{
return 1
}
}
}
return 0
}
/// Handle Setting
ClassMethod HandleSetting(production As %String, item As %String, class As %String, target As %String, varName As %String, value As %String) As %Status
{
if ##class(Ens.Config.DefaultSettings).IdKeyExists(production, item, class, varName, .id)
{
set defaultSetting = ##class(Ens.Config.DefaultSettings).%OpenId(id, , .sc)
if $$$ISERR(sc)
{
write "Failed to get default setting named '",item,":",varName,"': ",$SYSTEM.Status.GetErrorText(sc),!
return sc
}
if defaultSetting.SettingValue = value
{
write "Skipping '",item,":",varName,"' as it already exists with the same value",!
return $$$OK
}
write "Updating '",item,":",varName,"' from '",defaultSetting.SettingValue,"' to '",value,"'",!
}
else
{
set defaultSetting = ##class(Ens.Config.DefaultSettings).%New()
set defaultSetting.ProductionName = production
set defaultSetting.ItemName = item
set defaultSetting.HostClassName = class
set defaultSetting.SettingName = varName
set defaultSetting.Deployable = 1
write "Creating '",item,":",varName,"' from '",defaultSetting.SettingValue,"' with value '",value,"'",!
}
set defaultSetting.SettingValue = value
set sc = defaultSetting.%Save()
if $$$ISERR(sc)
{
write "Failed setting default setting named '",item,":",varName,"' to '",value,"': ",$SYSTEM.Status.GetErrorText(sc),!
}
return sc
}
/// Rmove Setting from production
ClassMethod RemoveSettingFromProduction(tProduction As Ens.Config.Production, item As %String, target As %String, varName As %String) As %Boolean
{
#dim tItemObj as Ens.Config.Item = tProduction.FindItemByConfigName(item, .sc, 1)
if '$IsObject(tItemObj)
{
write "Failed to get item '",item,"': ",$SYSTEM.Status.GetErrorText(sc),!
return 0
}
for i = 1:1:tItemObj.Settings.Count()
{
#dim tSetting As Ens.Config.Setting = tItemObj.Settings.GetAt(i)
if (tSetting.Name = varName) && (tSetting.Target = target)
{
do tItemObj.Settings.RemoveAt(i, .success)
return success
}
}
return 0
}
}
- Log in to post comments
这个函数是java调用IRIS上的方法或者routine。
比如有一个routine的名字是NativeRoutine,routine中有一个方法的名字是fnString,方法调用的参数是"World"
那么从Java调用就可以:
String stringVal = irisjv.functionString("fnString",routineName,"World");详细文档可以参考:https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=BJAVNAT_call
- Log in to post comments
由以上信息不足以判断安装失败的原因。从我的经验看,最主要安装失败的原因是安装版本和操作系统不匹配造成。
请根据文档 https://docs.intersystems.com/irisforhealth20221/csp/docbook/DocBook.UI… 确定安装包支持的操作系统。
并同时注意安装IRIS的服务器芯片组,比如是x86-64 或 ARM64等,需要对应不同的安装文件。
- Log in to post comments
可参考文档:https://docs.intersystems.com/ens201817/csp/docbook/DocBook.UI.Page.cls…
提供以下方法/工具分析sql性能:
SQL运行时统计:生成查询执行的性能统计。
索引分析器:显示当前命名空间中所有查询的各种索引分析器报告。这显示了Caché SQL要如何执行查询,让你对索引的使用情况有一个整体的了解。这个索引分析可能表明,你应该添加一个或多个索引来提高性能。
显示计划:显示SQL查询的最佳(默认)执行计划。
备选显示计划:显示一个SQL查询的可用备选执行计划,并附有统计数据。
索引优化选项:可用FROM子句来管理所有的条件,或者用%NOINDEX作为单个条件的前导。
并行查询处理 :可用%PARALLEL关键字的FROM子句选项,允许多处理器系统在各处理器之间划分查询执行。
- Log in to post comments
请参考文档:https://docs.intersystems.com/irisforhealth20231/csp/docbook/Doc.View.c… 提供更多ODBC 日志以供错误排查。
也可以在菜单中选择系统操作 -- 系统日志 -- xDBC错误日志查看。
还可以尝试使用其他三方ODBC工具,同样加载该odbc驱动,看是否报错。
- Log in to post comments
是的使用了索引,主要过程为:通过IndexBGRQ,将BGRQ对应的ROWId写入 临时文件A
再遍历临时文件A中的RowID,查询DataMaster中的内容。
- Log in to post comments
可以将Java Gateway Service的日志打开,设置方法是在Production管理页面选中该组件上,在设置中设置日志文件(包括路径和文件名称)。如果问题再次出现,我们可以对日志文件进行分析,开启之后请注意该文件的大小增长。
另外,linux的Dynamic TCP port范围是32768~60999,可通过下面的命令进行查询,例如(在RedHat7.9下),
sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
在这个范围内的tcp端口号可能会被系统动态分配给其他进程使用,所以建议咱们更改一个不在此范围内的端口号。
- Log in to post comments
这个具体要看代码Fetch2函数的第4行是什么,有可能是在query中使用了Unicode字符,可以尝试使用PrepareW替代Prepare函数,也有可能某些函数仅支持$CHAR(0) 到 $CHAR(255),比如Base64 encoding。
或者在安装的时候需要选择Unicode而不是8-bit的安装参数。
具体原因需要根据源代码分析。
- Log in to post comments
建议在Unix上使用JDBCGateway,因为目前只有iODBC支持Unicode
下面是示例代码使用JDBCGateway
#dim %JDBCGateway As %Net.Remote.Java.JDBCGateway
set conn = $system.SQLGateway.GetJDBCConnection("TestConn")
if (conn = "") quit 0
set cmd = "select c1,c2,c3 from wrc943403 where c1 = ?"
set st = %JDBCGateway.prepareStatement(conn, cmd)
do %JDBCGateway.setString(st,1,9) // assign c1 to 9
set res = %JDBCGateway.execQuery(st)
if (res) {
set error = %JDBCGateway.getErrorText()
write "error = ",error,!
quit
}
while %JDBCGateway.next(st) {
set c1 = %JDBCGateway.getString(st,1)
set c2 = %JDBCGateway.getString(st,2)
write "c1=",c1,!
write "c2=",c2,!
}
write $system.SQLGateway.DropConnection("TestConn")
quit- Log in to post comments
如果是运行在IRIS下,可以参考使用JSON Adaptor,详细文档可以参考这里
比如要使用包含下划线_的字段,可以使用%JSONFIELDNAME 定义
更多有关%JSON.Adaptor 类定义可以参考这里
- Log in to post comments
使用Python Binding,参见文档 https://cedocs.intersystems.com/ens20162/csp/docbook/DocBook.UI.Page.cls?KEY=GBPY_intro
例如一些基本的操作:
- 创建数据库连接
conn = intersys.pythonbind.connection() conn.connect_now(url,user,password, None) database = intersys.pythonbind.database(conn)
- 打开存在的对象
person = database.openid("Sample.Person",str(id),-1,-1) - 创建新的对象
person = database.create_new("Sample.Person", None) - 设置对象属性或者取值
person.set("Name","Doe, Joe A") name = person.get("Name") - 运行一个方法
answer = person.run_obj_method("Addition",[17,20]) - 保存对象
person.run_obj_method("%Save",[]) - 获取保存对象的id
id = person.run_obj_method("%Id",[]) - 执行一个查询
sqlstring ="SELECT ID, Name, DOB, SSN \ FROM SAMPLE.PERSON \ WHERE Name %STARTSWITH ?" query = intersys.pythonbind.query(database) query.prepare(sqlstring) query.set_par(1,"A") query.execute(); while 1: cols = query.fetch([None]) if len(cols) == 0: break print cols
同时可以在安装目录的/dev/Python/samples/ 下查看更多例子,比如
- CPTest6.py — Process the result set of a ByName query.
- CPTest7.py — Process the result set of a dynamic SQL query.
- Log in to post comments
可以通过下面网址直接打开SOAP 向导页:
{serverurl}/isc/studio/templates/%25ZEN.Template.AddInWizard.SOAPWizard.cls?Namespace={namespace}
比如:http://localhost:52779/isc/studio/templates/%25ZEN.Template.AddInWizard…
也可以在VSCode里添加用户自定义的快捷键:
"objectscript.conn": {
"links": {
"Portal Explorer": "${serverUrl}/csp/sys/exp/%25CSP.UI.Portal.ClassList.zen?$NAMESPACE=${ns}",
"SOAP Wizard": "${serverUrl}/isc/studio/templates/%25ZEN.Template.AddInWizard.SOAPWizard.cls?$NAMESPACE=${ns}"
},
}参考文档:https://intersystems-community.github.io/vscode-objectscript/configuration/#server-actions-menu
另:BPL,DTL一般直接在浏览器中进行编辑,完全不需要VSCode。
- Log in to post comments
Message Router的主要功能是消息路由,可以根据传递消息的不同内容将消息发送给不同的目标。
它不能用于监视task schedule任务执行的情况。
如果要通过代码监视任务执行情况可以:
set rs=##class(%ResultSet).%New("%SYS.Task.History:TaskHistoryForDay")
set startH=$ZDH("02/01/2017")
set endH=$ZDH("02/28/2017")
do rs.Execute(startH,endH)
;do rs.Execute() ;; or take all hisotries
while rs.Next() {
write rs.Get("Task Name"),":",rs.Get("Result"),!
}- Log in to post comments
直接用%XML.Reader,可以通过设置它的 SAXSchemaSpec 参数指定 schema。
读取xml就可以了。
USER>set reader = ##class(%XML.Reader).%New()
USER>set reader.SAXSchemaSpec = "c:\tmp\a.xsd"
USER>set sc = reader.OpenString(XMLstr)
USER>do $System.OBJ.DisplayError(sc)- Log in to post comments
最新版安装指南请参考:Linux 系统IRIS安装总结
- Log in to post comments
请参考文档:Emergency Access
使用此方法重置密码。
- Log in to post comments
在Production中使用函数请参考文档:https://docs.intersystems.com/irisforhealth20212/csp/docbook/DocBook.UI…
如何自定义函数可以参考:https://docs.intersystems.com/irisforhealth20212/csp/docbook/DocBook.UI…
- Log in to post comments
试试扩展函数,里面使用%Ensemble获取,同时还有%System中可以获取更多系统信息。
再在rule中使用这个扩展函数。
Class utils.functions Extends Ens.Rule.FunctionSet
{
ClassMethod GetSess()
{
s s=%Ensemble("SessionId")
&SQL(Select SourceConfigName into :sess from Ens.MessageHeader where ID=:s)
Quit sess
}
}- Log in to post comments
既然是再研究,何不试试我们最新的InterSystems IRIS。如果以前研究过Caché 会发现,IRIS 还是同样的架构、同样的开发语言、同样的global存储,只是人机交互更流畅、性能更快,增加更多扩展,机器学习、python、丰富的外部BI工具适配、容器化部署、API管理等等等等,都等着你探索。
要想体验就在社区主页左边,选择下载InterSystems IRIS。
WRC可以说是我们对客户的支持中心,所以只面对我们的客户开放注册。
- Log in to post comments
不知道你在哪里找文档找了一个星期,可以直接去docs.intersytems.com,选择你要对应的版本,然后再查找框里输入jdbc url, 比如我在最新的InterSystems IRIS for health 2021.2的文档中查到:
jdbc:IRIS://<host>:<port>/<namespace>/<logfile>:<eventclass>:<nodelay>:<ssl>
比如:
jdbc:IRIS://127.0.0.1:1972/User
- Log in to post comments
我们推荐的高可用性方案是Mirror。也就是配置至少主备两个镜像成员+灾备异步镜像成员,可选配置多个异步报表镜像成员以及多个灾备异步镜像成员。
当然同时推荐的是建立健全的外部备机机制,以及使用外部备份进行恢复的演练,手动升级灾备异步镜像成员为主机的演练。
对于大型应用一般使用ECP创建多台应用服务器,进行多用户的负载分配,再将多台应用服务器连接至数据服务器,数据服务器进行高可用性配置。
- Log in to post comments
“shutdown standby member“的操作应该与上面提到的"当备库因为主机故障或者网络隔离等因素导致不可用"是不一样的:
shutdown standby member是一个主动的将备机下线的过程,这个过程中,主机已经主动的将备机标注为“非激活”状态,这个时候主机不会等待备机的收到journal的信号,所以不会影响主机上的业务。
”当备库因为主机故障或者网络隔离等因素导致不可用“ 这个过程是一个被动不可预料的情况,这种情况下会有下面情况发生:
1. 在Qos规定的时间内,主机收到了备机的反馈信息,主机继续业务。
2a.在Qos规定时间内主机没有收到备机反馈信息,但是从仲裁服务收到冲裁服务仍能正常连接到备机的信息。会切换镜像状态监控模式为ISCAgent模式,业务访问主机不受影响。

2b. 在Qos规定时间内主机没有收到备机反馈信息,并且从仲裁服务也没有收到备机的情况,主机切换为ISCAgent监控模式,将备机置为trouble状态,主机继续承载业务。备机也会切换为ISCAgent监控模式,尝试重连主机。

3. 在Qos规定时间内主机没有收到备机反馈信息,也没有收到仲裁服务的信息。主机置为trouble状态。此时如果备机仍然保持和仲裁之间的连接,备机将会升级为主机,接管业务访问。当与原主机恢复连接后,会强制将原主机关机,以提醒管理员需要检查主机丢失连接的原因。

值得注意的是,IRIS/Caché 并不是直接将Journal的内容写入磁盘中的数据库文件中,Journal记录的是数据库的所有变化内容,而Write image journal(WIJ文件)则记录的是所有在内存中变化的但是还没有写到数据库的内容,写入磁盘的过程分两步,第一步将更新的内容从内存写入WIJ文件,WIJ标记准备写入磁盘的blocks 。第二步将标记的blocks写入磁盘,写完后,将第一步的标记清空。写入journal的过程是将journal buffer中记录的数据库内容的变化写入磁盘中的journal文件中。也就是说没有打开Journal功能的数据库文件依然是可以写入数据的(当然没有打开Journal功能是不能配置Mirror的)。
所以,说了那么多,对于终端用户基本上是感觉不到上面各种主备切换情况发生的。但是在系统终端是可以记录到的。
- Log in to post comments
Production 重启的过程包括了关闭Production和启动Production。
关闭Production的过程需要把所有活动Job中的消息放入队列中,这个过程如果发生在业务量非常大的时候,也就是job非常多,每个job中的待处理消息很多的时候会花费很长时间。
而启动Production的过程会逆向关闭的过程,恢复关闭前的job以及的消息,同样这个时间与关闭production前业务繁忙、队列的大小有关。
当关闭的时间过长,会提示是否需要“强制”关闭production,如果强制关闭,后果就是无法保证恢复关闭production前的业务。
如果想等待或设置较长的等待时间,可以在terminal中执行 Ens.Director 类的StopProduction函数:
ClassMethod StopProduction(pTimeout As %Numeric = 10, pForce As %Boolean = 0) As %Status
第一个参数设置超时时间,第二个参数设置是否强制关闭。
- Log in to post comments
不知道你问题里的日志是不是指的journal文件。
没有直接读取journal的方法,只有类似MirrorDejournal的方法对写入异步镜像成员的journal进行操作,
比如将内容写入到你自己的一个数据global中。
在写journal的同时将journal中记录的内容存放在另一个地方。
具体做法可以参考社区里的另一篇文章:CDC系列之一 :使用Dejournal Filter在InterSystems IRIS/Caché上通过Mirroring实现CDC功能
对于较早版本还没有镜像Mirror的配置,则可以使用Shadow服务器的Dejournaling filter routine。
可以参考:CDC系列之二 :使用Dejournaling filter routine在Caché上通过Shadow实现CDC
- Log in to post comments
InterSystems 没有提供产品原型工具,请百度。
- Log in to post comments
IRIS 没有默认的删除备份文件的任务。
你可以通过命令$zf(-100)或者%File下面的Delete方法实现通过ObjectScript删除磁盘文件的操作,再将代码添加入定时任务中实现定时删除。
同时注意,不建议在生产环境将IRIS自带的在线备份工具作为灾备恢复使用,因为该工具仅仅备份 IRIS.Dat 中的内容,而这些内容对于灾难恢复是不够的。
推荐使用外部备份的方式进行备份,详细内容参见(https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.U…)
- Log in to post comments
可以参考 %SYS.Journal.File.cls 这个类下的代码是如何使用$$$JRNNEWVAL(Address) 、$$$JRNOLDVAL(Address)的。
- Log in to post comments
使用的产品版本是什么?Dejournal filter是在Mirror report类型的异步镜像成员做的还是在shadow上做的?不同给的版本对应不同的处理方式。
同时还是建议将问题提到WRC:support@intersystems.com,他们会给予更详细的错误分析以及解决方案。
- Log in to post comments
