At the moment it's able to recode locally or remote mounted 8-bit database to Unicode one. All activity occurs at the Unicode system.

This approach was feasible because the only global collation we used in 8-bit database was Cache Standard. Use we something else (e.g. Cyrillic2), more complex solution would be needed, something like Pavel and Ivan briefly described.

I transfer large globals in several concurrent jobs; avg speed is ~ 8MB/s. As to journals, their transfer is times slower, while I try to compensate it using "economical" approach (with filtration) mentioned above.

My toolkit is under construction yet. At the moment it's able to recode locally or remote mounted 8-bit database to Unicode one. All activity occurs at the Unicode system.

Ivan, 

I'm just curious: how long it took to transfer 2TB?

IMHO, the main problem in such endeavors is not how to recode the database as it's always possible somehow, but how to avoid long interruption of 24x7 workflow. In my case I'm developing a tool which scans journal files and remotely applies the changes to those globals that are already was (or being) transferred to Unicode database. Async Mirroring with dejournal filtration could be used instead, although I don't want to use it for several reasons. 

Hi Alexi, 

It sounds like you are wanted to:

  • run the task in Caché Task Manager which does external call to some *.bat/.cmd file (so, calling out Caché)
  • this .bat file should run some code inside Caché (so, calling in Caché back again)

This scenario is possible (despite some security caveats), but (pardon me) - just rises unneeded difficulties. If your task can be solved easier than it initially seemed to be, why not take the easier (and more secure) way?

Evgeny --
Thanks, I've already tried it before writing here. My current settings were:

 Prospect Accepted
 Unsubscribe from all except privacy, security, and account emails

while I still received eMails about new challenges, etc. Just tried to turn off "Prospect Accepted", guessing that it can help, but this setting seems to be unswitchable (still active). It is hardly the source of a "problem" as I was never notified on the "prospect" stuff, even don't know its meaning.

All these eMails are not a great deal of disturbance, I just dislike when I can't control their frequency.  I don't insist on correction, if it causes extra efforts of your team - just sign me off from GM.

Thanks, Larry,
Adding your variant to the "code base".
As most of others, your solution needs *  length correction  * which I'm putting aside from Xecute to improve readability.

lpad(number=1,length=4)
  new code,i,z,sign
  set number=+number
  set sign="" set:number<0 sign="-",number=-number
  set code($increment(code))="w sign_$tr($j(number,length),"" "",""0"")"
  set:length<$l(number) length=$l( number) ;* length correction *
  set code($increment(code))="w sign_$e(1E"_length_"+number,2,*)"
  set code($increment(code))="w sign_$e(10**length+number,2,*)"
  set code($increment(code))="w sign_$e($tr($j("""",length),"" "",0)_number,*-length+1,*)"
  set code($increment(code))="s $P(z,""0"",length)=number w sign_$E(z,*-(length-1),*)"
  for i=1:1:code write code(i),"  => ",?60 xecute code(i) write !
  quit

If we are looking for more generic solution, e.g. pad <number> by zeroes yielding to the field of given <length>, let's try:

lpad(number=1,length=4)
  new code
  set code($increment(code))="w $tr($j(number,length),"" "",""0"")"
  set code($increment(code))="w $e(1E"_length_"+number,2,*)"
  set code($increment(code))="w $e(10**length+number,2,*)"
  set code($increment(code))="w $e($tr($j("""",length),"" "",0)_number,*-length+1,*)"
  for i=1:1:code write code(i)," => " xecute code(i) write !
  quit

Some  results are:

for n=999,9999,99999 d lpad^ztest(n,4) w !
 
w $tr($j(number,length)," ","0") => 0999
w $e(1E4+number,2,*) => 0999
w $e(10**length+number,2,*) => 0999
w $e($tr($j("",length)," ",0)_number,*-length+1,*) => 0999
 
w $tr($j(number,length)," ","0") => 9999
w $e(1E4+number,2,*) => 9999
w $e(10**length+number,2,*) => 9999
w $e($tr($j("",length)," ",0)_number,*-length+1,*) => 9999
 
w $tr($j(number,length)," ","0") => 99999
w $e(1E4+number,2,*) => 09999
w $e(10**length+number,2,*) => 09999
w $e($tr($j("",length)," ",0)_number,*-length+1,*) => 9999

Only the first solution (John's one) provides valid result even with "bad" input data ($length(99999) > 4). Others can't be amended this way without extra efforts irrelevant to this tiny task. Just to complete it:

lpad(number=1,length=4)
 new code,i
 set code($i(code))="w $tr($j(number,length),"" "",""0"")"
 set code($i(code))="w $e(1E"_$s(length>$l(number):length,1:$l(number))_"+number,2,*)"
 set code($i(code))="w $e(10**$s(length>$l(number):length,1:$l(number))+number,2,*)"
 set code($i(code))="w $e($tr($j("""",$s(length>$l(number):length,1:$l(number))),"" "",0)_number,*-$s(length>$l(number):length,1:$l(number))+1,*)"
 for i=1:1:code write code(i)," => ",?60 xecute code(i) write !
 quit

Now all solutions become correct even with the <number> >= 99999, but only the first one keeps its simplicity.

Rosti,

Your third option is the most generic and seems to be followed if you are developing from scratch because it keeps the door open for future extensions. E.g., imagine that a venue aggregator which provides on-line event search / ticket selling service would like to work with you. If you deploy your app as a separated instance or database for each venue, you would implement multi-database search for aggregating the events. 

Such technologies as containerization can help to organize the development/testing/[deployment(?)] better, but IMHO should not be of great impact on the app/database design. "The cart should not run in front of the horse", as old Russian proverb says. 

Hi Pete,
we have implemented the model that's very similar to yours with slight differences:

  • we use it at home only, where we have several development and testing Caché instances;
  • most of them are not connected using Mirroring and/or ECP;
  • all Caché users are LDAP users, so we need not bother of creating/modifying users on per instance basis;
  • one instance is used as a repository of roles definition (so called Roles Repository); it "knows" about each role that should be defined on each instance; this repository is wrapped with a REST service;
  • each role has a "standard" name, e.g. roleDEV, roleUSER, roleADM; these names are used in LDAP users' definition and retrieved during LDAP authentication process;
  • the resources lists for each standard role are stored in the Roles Repository; those lists are associated with instance addresses (server+port), so each role can be differently defined for different Caché instances; therefore, a user with the role roleDEV  can have different privileges on different servers;
  • each Caché instance queries the Roles Repository at startup; after getting the current definitions of its roles, it applies it to its Caché Security database.

This solution is used in our company's Dev & Testing environment more than a year without great problems. It is rather flexible and doesn't depend on proprietary transport protocols. The only drawbacks found are:

  • the Roles Repository is automatically queried at Caché startup only, so if something in role(s) definition(s) should be changed on fly, manual querying is needed;
  • sometimes InterSystems introduces new security caveats with the new versions of its products; one of them was a subject of an article here: https://community.intersystems.com/post/implicit-privileges-developer-ro....