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 · 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 · 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 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 20, 2017 go to post

Yet another variant of quotes duplicator (pure Mumps): 

dupP(str)
  quit:str'[$c(34) str
  new strdup,set strdup="" for j=1:1:$length(str,$char(34))-1 set strdup=strdup_$piece(str,$char(34),j)_$char(34,34)
  quit strdup_$piece(str,$char(34),j+1)

Quick testing showed that the quickest variant is my initial $$dupquote(), $$dupocc() is very close (5-15%  slower), $$dupP() took 3d place (2 times slower), $$QT() is the slowest (not surprise as it uses recursion).

It was interesting to find that the good quality of $replace() implementation allowed it to beat $zutil(144,) "system" function. While I'll keep using it, analyzing Vitaliy's code I've found the error in my own $$dupP().

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 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 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 2, 2017 go to post

It all depends.

Attempts to connect a port or even to connect a super-server using potentially wrong password spoil Cache Audit with nasty records, so if they are done frequently those records can easilly overfill the Audit.

Several years ago I faced the similar problem in opposite direction: how to let load balancer recognize Cache instances which are no longer alive to remove them from its list. Load balancer was to distribute super-server connections as well as web based ones. The idea of polling 1972/tcp was dropped as soon as I recognized its impact on auditting. So I used a web app which allowed unauthenticated access for the simple reason that if 57772/tcp port had reasonably answered, 1972/tcp should be accessible as well. There were no firewall(s) between the load balancer and application servers, therefore I was sure that there were no external "forces" that could prevent them from answering. The solution was deployed on a couple of different load balancers and showed its robustness on a farm of 4-7 application servers and 1000-3000 concurrent users.

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 · 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 · 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

We have a TASKMGR's task that purges such kind of globals daily. (Sub)globals are cleaned using the following criteria:

  • Global has a structure of @name@(PID)
  • Global is listed in predefined "known globals" list
  • Caché process identified with PID is not running.
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 · 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 · 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 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 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 10, 2017 go to post

Robert,

thank you for sharing your experience. I'm just curious:
- What was the final database size in GB?
- What tool will be used for data analyzis? If a homemade one, will it be based on Caché?

Alexey Maslov · Aug 1, 2017 go to post

If +value=value

This classic code is good for checking if the value is canonical number, while the term number can be interpreted in some other ways, e.g. a number in scientific format, double precision number, etc. Caché has a set of out-of-the-box functions to perform some of these checks, e.g. $isvalidnum, $isvaliddouble, $number, $normalize.

So, the answer depends on topic starter's conditions.

E.g., if I need to check if a number is a numlit (scientific numbers and numbers with starting zeroes are allowed), I'd use `$isvalidnum(x)`. Addition check on being integer (an intlit) can look like: `$isvalidnum(x)&&(+x\1=+x)`. Here are some testing results: 

USER> w !,?6,"Is number?",?20,"Is integer?",?35,"Is canonic?"
USER> for x="001a","002",0,1,"-1.2","+1.3","1E3" w !,x,?10," ",$isvalidnum(x),?20," ",$isvalidnum(x)&&(+x\1=+x),?35,x=+x
 
      Is number?    Is integer?    Is canonic?
001a       0         0             0
002        1         1             0
0          1         1             1
1          1         1             1
-1.2       1         0             1
+1.3       1         0             0
1E3        1         1             0

In other conditions I'd write another code. There is no universal answer to topic starter's question.

P.S. As to "Annotated MUMPS Standard" (http://71.174.62.16/Demo/AnnoStd):
An intlit is not necessarily a canonic representation of a number.

numlit is not necessarily a canonic representation of a number.

The reduction to a canonical numeric representation involves (colloquially) the removal of any redundant leading and trailing zeroes, the conversion of exponentionential notation to "mantissa only" notation, and the reduction of any leading plus (+) and minus (-) signs to at most one leading minus sign (see also Numeric interpretation of data).

Alexey Maslov · Jul 19, 2017 go to post

Method with a variable arity can also pass through their arguments to the next method

Good point!

Wouldn't it be great if $classmethod and $method functions support variable arity as well? (Today they don't, Cache 2017.1). How to use them with such a methods without this feature?

PS. Sorry for off-topic.

Alexey Maslov · Jun 16, 2017 go to post

Via SSH (putty, etc), am I right that you need to call csession <instance> to enter the Caché terminal? If so, there is no talk about SSH all. 

Caché has got embedded libssh2.dll/.so ages ago. Why not implement internal SSH server which can be a reasonable replacement for outdated (and Windows only) telnet one? It seems that some other projects (besides Web terminal) would take advantage from it.

Alexey Maslov · Jun 8, 2017 go to post

As large as 20 chars in expression mode.

/// ("鑔" = chǎ - small cymbals) * ("斚" = jiǎ - a small ancient stone cup for libations)
ClassMethod main() As %String [ CodeMode = expression ]
{
$a("鑔")_$q*$a("斚")
}
Alexey Maslov · Jun 2, 2017 go to post

Our customers are to check DB integrity on regular basis, usually weekly, while I don't remember a case when it showed errors which were not evidient without it (<DATABASE> errors in error and console logs, etc).

Last time when I had an opportunity to use ^REPAIR was about 1.5 year ago, when our support specialist defragmented free space in a database under Caché 2015.1.2. The bulletin from InterSystems about the possibility of defragmentation errors arrived a bit later... Thanks to backup performed before the defragmentation, the opportunity to use ^REPAIR was closed that time :) After upgrade to 2015.1.4 no errors of such kind were detected in the field.

The faults of Integrity check are:
- when there is some concurrent users' activity it may provide false positives in per database summary report (Errors found in database...) while there are no real errors neither in database nor in per global report;
- (mostly about TASKMGR): there is no way to include into the task completion reports (which can be e-mailed) any information from the task, e.g., about errors found by Integrity. 

Alexey Maslov · Jun 1, 2017 go to post

Sergey, you are doing a great job popularizing our (unfairly) niche technology!

Despite the article was published on private resource,  such a phrase: 

They were first introduced in 1966 in the M(UMPS) programming language (which later evolved into Caché ObjectScript, COS), which was initially used in medical databases.

sounds (at least for me) as a disrespect to many talented people who contributed to the technology in terms of many other M implementations. Some of them already gone...

Truth should sound like this: COS was developed by InterSystems as a superset of M (see http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…  original language is still accepted by developers' community and alive in a few implementations. There are several signs of activity around the web: MUMPS Google group, user group (http://mumps.org/), effective ISO Standard (http://71.174.62.16/Demo/AnnoStd), etc.