Jeffrey Drumm · Sep 17, 2019

Mirror Backup vs. R/O Database Behavior - Scheduled Task for HL7 Message Dumps

I've created a scheduled task that dumps HL7 messages to flat files. It uses the OutputToLibraryStream() method of EnsLib.HL7.Message. I'd like to be able to run it on a mirror member host whether it's primary or backup.

When run against a database that's mounted read-only, it works fine. However, when I attempt to run it against a mirror backup database, I get the following error:

#5002: Cache error: <PROTECT>%LoadData+7^EnsLib.HL7.Message.1

Tracing that back to the source, I find:

Lock +(^EnsLib.H.MessageD(id)#"SE"):$zu(115,4)

Can someone help me understand how locking might behave differently on a "conventional" R/O database vs. a mirror backup R/O database? I can rationalize the behavior due to the fact that dejournaling is still doing sets/kills and that a lock would potentially mean bad things for mirror consistency, but I don't know why I wouldn't see this behavior on a database that is purposely mounted R/O.

Oh, and I'd also like to make this work :D

0 282
Discussion (9)2
Log in or sign up to continue

Hello Jeffrey,

You're absolutely right that normal read only databases and mirrored databases operate differently. It is not possible to take out locks for a mirrored database on a backup failover member as locks would prevent the member from taking over as primary.

If you have a DR async and take out a lock on a mirrored database, then when you try to promote to failover member you'll see a message like this:

Unfortunately, that means I don't have a workaround for you unless you have a DR where you can dump the messages instead.

Thanks to @Pete Greskoff who I talked to about this behavior.

Thanks Vic for the confirmation and explanation.

What I still can't figure out, though, is why a lock is needed to read a message body from the database and write it to flat file. Practically speaking I don't need the lock ... the message has almost reached its sell-by date and will be purged from the database within the week. It's never going to be modified again.

A DR Async/Reporting member is a possibility, but not for the near term ...

Looking at the docs, %OpenId() accepts a  concurrency argument. 0 is "no lock," but when using it on a mirror backup I get:

ERROR #939: Insufficient privilege for object access 'EnsLib.HL7.Message::%Open'

Not sure what privilege I can be granted to allow it as I'm running it with the %All role ... but I get the feeling I'm barking up the wrong tree.

I'm not sure exactly what your steps were - where did you see that error? I have no problem opening objects without locking in a mirrored database on a backup failover member:

MIR>s x= ##class(Sample.Person).%OpenId(1,0)

MIR>w x

Hey, Vic. Thanks again ...

I'm opening an HL7 message body.

Set tMsg=##class(EnsLib.HL7.Message).%OpenId(tId,0)

I'm then writing it to a stream object:

Set tSC=tMsg.OutputToLibraryStream(tObj)

The error appears when outputting to the stream.

While I suspect this is related to the read-only / mirrored-ness of the backup failover member, I would recommend reaching out to the WRC for a more thorough investigation.

Alex and Alexey suggested a nice workaround as well.


I'd try to define namespace global mapping, something like this:



YOURNSP - your namespace
YOURNSPDB - database where ^Ens* globals are stored (mirrored one)
YOURNSP-LOCKS - database ^Ens* globals locks will be associated with (local one, RO or RW, it doesn't matter).

The similar definitions should exist for each global groups that can be locked.

Disclaimer: I didn't try this trick myself with mirrored DBs. It might help to bypass the locking problem, while not guarantee against other suprises one can face with running something on Mirror Backup member. Consulting with WRC might be of use... 


That's exactly what we did to offload message archiving to our DR server.

We created another namespace (R/W, non-mirrored) and mapped the following globals from the original namespace with the mirrored R/O database: 
EnsLib.*, EnsLib, EnsHL7.Segment, EnsHL7.Schema, EnsHL7.Description, EnsEDI.*, EnsEDI.X12.Schema, EnsEDI.X12.Description, EnsEDI.Schema, EnsEDI.Description, EnsEDI.ASTM.Schema, EnsEDI.ASTM.Description, Ens.MessageHeaderI, Ens.MessageHeaderD, Ens.MessageBodyI, Ens.MessageBodyD, CacheMsgNames, CacheMsg.

Then, we run the message archiving task in the other namespace without issues.