Separating Code, and Data databases in Installer Manifest using a containerized IRIS and durable %SYS feature
Hello,
The title says it all. I’m building an IRIS image with docker-compose using a separate Dockerfile. Pretty straightforward procedure: I import a Installer script inside the container containing a Installer Manifest I defined. Within the manifest, I create a namespace with code and data databases in separate locations. My intention is to keep the code database inside the container, so whenever I build the container, the imported code is replaced. The data, however, should be persistent.
Without setting the ISC_DATA_DIRECTORY environment variable, I can see from the Management Portal, after I built and started the container, that the databases are in separate locations as expected. For clarity’s sake, I defined that the code database (Namespace-Code) is in /code/NameSpace-Code/ directory and the Data is in /usr/irisssys/mgr/Namespace-Data
If I define the ISC_DATA_DIRECTORY environment variable and mount a corresponding docker volume, I would expect the Data db to be moved to the persistent volume and the Code db to be inside the /code folder withing the container. However, when doing so, for whatever reason, the Code db is moved to the persistent volume as well. When I checked the database locations from the Management Portal after enabling the durable %SYS feature, suddenly the Code database location was in /durable/irissys/db/code/NameSpace-Code/ and the data db was in /durable/irissys/db/usr/irisssys/mgr/Namespace-Data folder. It seems like the ISC_DATA_DIRECTORY variable value is appended in the beginning of the db path.
Is there anything I can do to prevent the Code db to be moved to the persistent volume and instead just move the Data db there instead? The Data db path is also a bit weird. It would be great if the Data db would be within /durable/irissys/mgr folder.
Any idea if I’m doing something fundamentally wrong with this?
If needed, I can copy and paste some docker configurations I use, even though they are pretty much copy and pasted from examples provided by InterSystems.
Just to clarify, everything is working great, but I just need the Code database to live within the container instead of being persistent.
Thanks for any insight and/or examples!
Kari
Comments
This is how Durable %SYS works, when it's initialized, it copies everything to the durable folder, including your db in mgr directory. And you can prevent it, by creating this database, just somewhere else.
Make code db read-only and it won't be moved as a part of Durable %SYS.
Yes, this is absolutely correct, the code checks the R/W status of the database. If it is set to read-only, it will not be copied up to the durable directory. The first version which has this feature are kits based off of IRIS 2021.2.
Thank you for the response.
It seems to work! I set the database permissions to read-only during my Installer script and it seems that the Code database is where it should be and is not persistent. Thank you very much for your responses.
I couldn't set the permissions via the Installer Manifest so I ended up writing a small ObjectScript script to achieve this. Here it is for future reference:
/// Change database permissions
/// <ul>
/// <li><var>dbDir</var> Path to the database.</li>
/// <li><var>mode</var> Permission mode. 0 = read/write, 1 = read-only. Optional</li>
/// </ul>
ClassMethod SetupDBPermissions(dbDir as %String, mode as %Integer = 0) As %Status {
New $NAMESPACE
Set origNs = $NAMESPACE
Set $NAMESPACE = "%SYS"
Set sc = $$$OK
Set db = ##class(SYS.Database).%OpenId(dbDir)
Write "Setting database permission for " _ db.Directory _ ". Setting ReadOnly from " _ db.ReadOnly _ " to " _ mode, !
Set db.ReadOnly = mode
$$$ThrowOnError(db.%Save())
Set $NAMESPACE = origNs
Return sc
}Can shorten to:
/// Change database permissions
/// <ul>
/// <li><var>dbDir</var> Path to the database.</li>
/// <li><var>mode</var> Permission mode. 0 = read/write, 1 = read-only. Optional</li>
/// </ul>
ClassMethod SetupDBPermissions(dbDir as %String, mode as %Integer = 0) As %Status {
New $NAMESPACE
Set $NAMESPACE = "%SYS"
Set sc = $$$OK
Set db = ##class(SYS.Database).%OpenId(dbDir)
Write "Setting database permission for " _ db.Directory _ ". Setting ReadOnly from " _ db.ReadOnly _ " to " _ mode, !
Set db.ReadOnly = mode
$$$ThrowOnError(db.%Save())
Return sc
}Hello,
I have a similar problem, but the solution described here doesn't seem to solve it.
I have installer.cls with manifest in it, where I create namespace and 2 db's (for code - updatable and data - durable). In docker-compose I use ISC_DATA_DIRECTORY to set durable volume. Also I use module.xml to create web application and load code.
I tried to set my code db permissions to read-only in iris.script after I setup new namespace and db's but then I have errors when loading classes to db.
I'm pretty new to IRIS, so will be grateful for any suggestions. Thank you.
iris.script
ZN"%SYS"Do$system.OBJ.Load("/opt/irisapp/App.Installer.cls","ck")
Do##class(App.Installer).Setup()
ZN"STORE"Do$System.OBJ.LoadDir("/opt/irisapp/src", "ck","",1)
//ZN "%SYS"//Do ##class(App.Installer).SetupDBPermissions("/code/storeCode/", 1)zn"%SYS"do##class(Security.Users).UnExpireUserPasswords("*")
do##class(Security.Roles).Create("CustomAdmin", "Custom admin role for OGS application")
do##class(Security.Roles).Create("Purchaser", "Custom purchaser role for OGS application")
do##class(Security.Users).Create("CustomSuperUser%40admin.com", "%All,customadmin", "password", "Custom SuperUser")
do##class(Security.Users).AddRoles("SuperUser", "%all,customadmin", 1)
zn"STORE"do##class(GroceryApp.utils.Seed).SeedRegions()
do##class(GroceryApp.data.Category).AddMultipleCategories()
zpm "load /opt/irisapp/ -v":1:1haltApp.Installer.cls
Class App.Installer
{
XData setup
{
<Manifest>
<Default Name="Namespace" Value="STORE" />
<Default Name="app" Value="store" />
<If Condition='(##class(Config.Namespaces).Exists("${Namespace}")=0)'>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}CODE" Data="${Namespace}DATA" Ensemble="">
<Configuration>
<!-- Durable data database -->
<Database Name="${Namespace}DATA"
Dir="${app}Data"
Create="yes"
MountRequired="true"
Resource="%DB_${Namespace}DATA"
PublicPermissions="RW"
MountAtStartup="true" />
<!-- Non-durable code database -->
<Database Name="${Namespace}CODE"
Dir="/code/${app}Code"
Create="yes"
MountRequired="true"
Resource="%DB_${Namespace}CODE"
PublicPermissions="RW"
MountAtStartup="true" />
</Configuration>
</Namespace>
</If>
<Namespace Name="USER" Create="no" Code="USER" Data="USER" Ensemble="0">
<Configuration>
<ClassMapping From="${Namespace}CODE" Package="GroceryApp" />
<RoutineMapping From="${Namespace}CODE" Routines="GroceryApp" />
</Configuration>
</Namespace>
</Manifest>
}
ClassMethod Setup(ByRef pVars, pLogLevel As%Integer = 0, pInstaller As%Installer.Installer) As%Status [ CodeMode = objectgenerator, Internal ]
{
Quit##class(%Installer.Manifest).%Generate(%compiledclass, %code, "setup")
}
/// Change database permissions/// <ul>/// <li><var>dbDir</var> Path to the database.</li>/// <li><var>mode</var> Permission mode. 0 = read/write, 1 = read-only. Optional</li>/// </ul>ClassMethod SetupDBPermissions(dbDir as%String, mode as%Integer = 0) As%Status {
New$NAMESPACESet$NAMESPACE = "%SYS"Set sc = $$$OKSet db = ##class(SYS.Database).%OpenId(dbDir)
Write"Setting database permission for " _ db.Directory _ ". Setting ReadOnly from " _ db.ReadOnly _ " to " _ mode, !
Set db.ReadOnly = mode
$$$ThrowOnError(db.%Save())
Return sc
}
}