Alexey Maslov · Aug 18, 2017 go to post

Thank you, Stephen.

I should confess that you are quite write: our code resides in .cls, .inc, .int and also in globals. As to my straightforward code, it is not written yet. At the moment I have a simple "search-in-global engine", which matching ability should be improved (regexp?) and replacement functionality should be added. If speed will turn to be a problem, I will use it with ^ROUTINE, ^rINC and ^oddDEF globals as well, while I dislike this idea and prefer to start with official API (%Dictionary classes). 

Alexey Maslov · Aug 18, 2017 go to post

Thank you, John.

Maybe we should look at RE/parser closer, while I'm not sure if it's able to cope with our great mixture of code (see my comment above). 

Honestly, string literals matching/replacing is only a small part of a bigger task of adding multi-language support to our HIS.

Alexey Maslov · Aug 22, 2017 go to post

Thank you, Robert.

In this case the PID saved in a file will be the PID of command processor (cmd) itself rather than the PID of an utility which it has invoked. If Cache process kill the cmd instance using this PID, the utility will continue its execution. Besides, there is no  parent-child relationship between the cmd and the utility, so /t switch (kill process tree) would not help.

Alexey Maslov · Aug 22, 2017 go to post

Thank you, Timothy.
Definitely this is a solution, while our case is a bit more complex. It can be several running copies of "dangerous" utility started by different users, so we can't select the actual one neither by executable name nor by its window title as it is started with "-nogui" option and has got no window.

An approach how to bypass this limitation that we are going to implement looks like this:

 lock +^offPID
 set rc=$zf(-2,"drive:\path\utility --par1 --parN")
 if rc=0 {
   get PIDs of all running copies of utility.exe
   if '$data(^offPID(PID1)) {
       set ^offPID(PID1)=$h // search a new PID (let it be PID1)
       job checkJob(PID1)
   }
 } else {
   process an error
 }
 lock -^offPID
 ...
 ...

сheckJob(pPID) // check if pPID is running
  for {
    get PIDs of all running copies of utility.exe
    if pPID is not listed {
       kill ^offPID(pPID)
       quit
     
   } elseif timeout expired {
       set rc=$zf(-1,"taskkill /pid "_pPID)
       if rc=0 { ... }
       else { process an error }
       kill ^offPID(pPID)
       quit

   } else {
      hang 30 // wait...
   }
}
quit
Alexey Maslov · Aug 29, 2017 go to post

Thank you, Eduard,

This is a good example of regexp usage. At the meantime a colleague of mine is working on JS based solution outside of Caché (regexp replacement in .xml file). It's not clear enough if porting it to COS (class sources regexp replacement) would add any benefit.

Alexey Maslov · Sep 5, 2017 go to post

Daniel,

you wrote that:

we need to communicate to the Authorization server with SSL

Is it really necessary for the first steps with Open ID / OAUTH in testing environment?

Alexey Maslov · Sep 6, 2017 go to post

Robert, 

You are quite right, but... life is life, and application can finish abnormally. In this case its temp globals are left till Caché restart, sometimes it may cause noticeable growth of CACHETEMP database. So, scheduling a nightly housekeeping task like ours makes some sense.

Alexey Maslov · Sep 6, 2017 go to post

I remember well times when routines used to start with  KILL ^CacheTemp*($JOB)

Our developers do the same, but modern versions of OSs usually have 6 digit PIDs, so it may take several days till PIDs would re-cycle their numbers.

PPGs are used, however sometimes they are not applicable, e.g. when information is passed between processes via temp globals.

Clean-up is a good idea, but can be fully implemented when (and only when) all app classes use the same before-exit-processing code, i.e. the app is always finish through one exit point. Sometimes it's difficult to achieve, while it's worth to.

A while ago I thought about some enhancements of my purging task:

  • automatic detection of too quick growing globals
  • automatic detection of globals which belong to its scope, i.e. have @name@(PID) format.

 Maybe I do it when have some spare time... the priority of this work is rather medium.

Alexey Maslov · Oct 2, 2017 go to post

It's better to use CACHETEMP mapped global, e.g. ^CacheTempUserYourGlobal($job,...).

BTW there is another option: having a wrapper job, pass it an array serialized to JSON, other steps are evidient. I used it in one of my projects, and it was quicker than using of ^CacheTemp* intermediate global, while performance gain was not too sufficient (AFAIR)

The approach with a global is apparently easier, so I'd use it if performance would not of great importantance.

Alexey Maslov · Oct 2, 2017 go to post

Only the web app's home page was requested, without any attempt to log on, so it didn't write any audit record. In contrast, any attempt to connect 1972/tcp always wrote a "Logon Failure" record described as "Authentication broken" or something like this.

Alexey Maslov · Oct 16, 2017 go to post

Both had the need to pass along data by references as memory was very limited
and processors were incredible slow (compared to today).

Robert, thank you for this quick historical excursion!

Just adding 2c: it seems that the reasons to introduce pointers were deeper than just resources saving:

  • it was the reasonable way to pass arrays back and forth,
  • it was the reasonable way to return several values from functions/subroutines.
Alexey Maslov · Oct 17, 2017 go to post

Thank you for taking part, Vitaliy!
BTW, your second solution should be amended:

dupocc(str) ; after the macro substitution    quit $s(str'[$c(34):str,1:$e($zutil(144,1,str),2,*-1))

 otherwise it would destroy numbers:

USER> set b=1 set c=$e($zutil(144,1,b),2,*-1) zwrite b,c
b=1
c=""
Alexey Maslov · Oct 25, 2017 go to post

You may need to make the CACHELIB database R/W to save the edit to %ZSTART

No, because %Z* routines are stored in CACHESYS database. The only exception is `%ZEN.*`. You may want to check `do SHOW^%NSP` output:

Routine name/Range        Type      Dirset              Target directory
-------------------------------------------------------------------------------
   ...
   [%SYS/:%Z)             INT                           c:\intersystems\cache\mgr\cachelib\
   [%Z:%ZEN.)             INT                           c:\intersystems\cache\mgr\
   [%ZEN.:%ZEN/)          INT                           c:\intersystems\cache\mgr\cachelib\
   ...

===============================================================================
               ... a square bracket [] means that
               left or right margin is inclusive, while a parenthesis
               means that the margin is exclusive...

Alexey Maslov · Oct 31, 2017 go to post

Stuart,

Your solution is really beatiful. As to speed, it's just a bit slower than the solution #1 (based on $replace) and ~ twice quicker than my solution #2 (pure Mumps). That's a real pearl to add to my snippets collection, thank you!

Alexey Maslov · Oct 31, 2017 go to post

Just a quick note: the last sample with $tr removes _all_ listed chars, so all blanks (rather than leading, trailing and repeationg) would be removed as well.

Alexey Maslov · Nov 3, 2017 go to post

Hi Mikhail, you've done a really nice job!

I'm just curious, why:

We don’t care about output to a file.

Wasn't it easier to parse mgstat's output file?

Alexey Maslov · Nov 6, 2017 go to post

You can calculate a hash for each string and index this hash value. If use pure COS for index building, you can find duplicates earlier, just during index building, without quiering the index afterwards.

Alexey Maslov · Nov 7, 2017 go to post

It depends.

If you don't care about uniqueness of this hash, you can use any algorithm (the quicker the better, MD5Hash or even CRC32) and store your index like normal non-unique one. On calculated hash match to already stored (indexed) hash value you just compare the correspondent strings to separate real matches from the collisions. Maybe it's worth to use whole string as an index for reasonably short strings.

Alexey Maslov · Nov 13, 2017 go to post

Hi Robert,

There are at least two additional exceptions:

3. JOB command with passing the symbol table to the spawned job

See http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…

When switch#2=1, only public variables of parent process are passed to its child.

4.  Class %SYS.ProcessQuery, query VariableByPid() (or$zutil(88,2,Pid,Variable) for old schoolers)

Only public variables of another process can be queried.

Alexey Maslov · Nov 13, 2017 go to post

...and spread over documentation

...Or undocumented, as it is in cases #3 and #4. 

It seems that it's possible to form a simple rule whether you can use a private variable: if you can consider its name to be evaluated at compile time, you can, otherwise - no:

1) Indirection: variable names (or expression that contains them) are evaluated at runtime.

2) Xecute: similar to #1. In contrast, $xecute() arguments names can be evaluated at compile time.

3) Child process's symbol table filled with switch#2=1: runtime.  In contrast, job's entry arguments names can be evaluated at compile time.

4) %SYS.ProcessQuery is dynamically querying other process's symbol table, so, run time only.

Alexey Maslov · Nov 23, 2017 go to post

The result set of Config.MapGlobals:List query can be restricted to the global of interest (pGlb) records if change the Names search criteria in Execute call to:

    set sc = rset.Execute(pNsp,pGlb_","_pGlb_"(*")

Snippet's code was amended.

Alexey Maslov · Nov 24, 2017 go to post

Hi John and Roman,

Although the answer is accepted, it seems that JOBEXAM can only help recognize that DBREST is still running, so the initial question about the completion time expectations would hardly be answered this way.

Roman, only your previous experience can help answering your question. BTW, "weakness" of your host as you described it can't be a reason of slow DB restoration as DBREST is single-threaded utility and mostly i/o bound one. The reasons for slowing DBREST down are usually: slow disk subsystem and/or slow LAN connection to network resource where .cbk files are stored.

Entry level SAS RAID and 1000BaseT LAN usually provide 40-50MB/s speed on DB restoration.

Alexey Maslov · Nov 29, 2017 go to post

Thank you, Eduard.

Is it possible to do something with an "old" Cache 2015.1?

I have no objection to upgrade, but according to our previous experience the movement of customers' sites will not be quick. So I'd like to find a solution which will be useable from 2015.1 to 2018.2.

Alexey Maslov · Nov 30, 2017 go to post

My first guess was that it concerned Application Monitor alerts only, while I was mostly interested in Health monitor sensors reading.

Alexey Maslov · Nov 30, 2017 go to post

Yes, I did.
It did not seem working, STOP / START of the System Monitor didn't help.
I lost my interest to Callback functionality because of its absence in newer Caché version, so I would not investigate it anymore.

Alexey Maslov · Dec 6, 2017 go to post

Just 2c to add. If a "national" locale is effective, e.g.

%SYS>zw ^%SYS("LOCALE","CURRENT")
^%SYS("LOCALE","CURRENT")="yruw"

and the setting was done before Caché restart:

%SYS>set ^SYS("NLS","Config","LocaleFormat")=1

you will get your national day name if don't forget to set localeopt=0, e.g.

USER>f localeopt=0,1 w localeopt," ",$ZD($h,12,,,,,,,,localeopt),!
0 Среда
1 Wednesday

It's also possible to achieve the same overriding format defaults for the current process (see $ZDATE description for details). I emphasized the need in Caché restart just because didn't find it in docs.

Alexey Maslov · Dec 7, 2017 go to post

This option: 

$ZD($h,12,,,,,,,,localeopt)

seems to be more safe as switching the default representation for $ZD($h,12) may ruin already existing functionality that is expecting English days' names.

To be honest, I'd prefer application level localization, just because not all possible languages are supported by Caché.

Alexey Maslov · Dec 20, 2017 go to post

Hi, Nikita,
I'm a new WebTerminal user, just installed v.4.7.3.

JOBEXAM has problems with it, and it seems that it's %X364 support again. Here is an excerpt from my session: 

CWTv4.7.3 ...

...

USER > zn "%SYS"

%SYS > d ^JOBEXAM

<NOLINE>^%X364

╠%X364 ; BINDING FOR ANSI X3.64 NAMESPACE, NOV/92 ; LRS952 06/07/05

%SYS > w $zv

Cache for Windows (x86-64) 2017.2 (Build 744U) Fri Sep 29 2017 10:58:27 EDT

I've checked %X364 source, it has got correct DAQ() code with this Caché version.

Waiting for your help and advice.