Written by

Senior Integration Specialist at EskoSystems
Question Kari Vatjus-Anttila · Feb 25, 2022

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

Dmitry Maslennikov · Feb 25, 2022

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.

0
Eduard Lebedyuk · Feb 25, 2022

Make code db read-only and it won't be moved as a part of Durable %SYS.

0
Steve Clay  Feb 25, 2022 to Eduard Lebedyuk

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.

0
Kari Vatjus-Anttila  Feb 28, 2022 to Steve Clay

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
}
0
Eduard Lebedyuk  Feb 28, 2022 to Kari Vatjus-Anttila

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
}
0
Anatoli Kosarau · Dec 17, 2024

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:1halt

App.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
}
}
0