go to post Dmitry Maslennikov · Jun 22, 2016 Studio reformats some class parts, such as Methods defenitions when you save it.But as I know feature Atelier, stores class definition as is, so you do not need more such option, just format class as you need, and it should keep so.
go to post Dmitry Maslennikov · Jun 17, 2016 As I've already had some experience in such systems in production, and one of our projects, has similar architecture exclude docker, just on windows, some physical servers with ECP-Client + CSPGateway + Apache, and one HAPorxy server for all of this server. And in this case all this scheme is quite static, and adding new ECP-client means some manual works, on all levels. But with docker I expect, just call something like this command docker-compose scale ecp=10and just get some new working instances, which just after gets their new web-clientsTo work it as microservices and split CSPgateway and Cache instance as different containers, I need to have simple package just only with CSPGateway, but FieldTest versions does not contain it. But yes sure, I think it is good way too, and in this case I can have more WEB-containers then Cache does, if it would be needed.
go to post Dmitry Maslennikov · Jun 17, 2016 Timur, you can look at my example at github which I wanted to use in to article about using docker but have not managed yet. In this example I have Dockerfile for ECP-client, which can be build for particular ECP-server. And with %Installer manifest possible to make backward data channel too, while we have already known about where our server placed, we can connect to him via %Net.RemoteConnect or something else and make new backward connection, the problem is in this case how to remove old ones. I played only on one machine. But in anyway, Ansible still could be useful, but in case to prepare servers to work in docker cluster, which should be prepared before we could use docker-compose and so on. And My example also contains web-server (apache), to have an access to this new instance. What I wanted to do then is to use some load balancer, HAProxy or traefik (as recommended Luca), to get one point access to my application, and dynamically expandable, without any manual operations, except scaling.
go to post Dmitry Maslennikov · Jun 14, 2016 Yes, such language extension is very useful, but there is one more feature less known, and unfortunately completely undocumented. Its structured system variables, some of them could me familiar it is: ^$JOB, ^$LOCK, ^$GLOBAL and ^$ROUTINE. More information in documentation here. But not all knows that it is possible to make your own such global. You should create routine with name SSVN"global_name" in %SYS namespace, for example SSVNZA for ^$ZA global and for example with this code below, you SET, GET, KILL any values in this global, and this global will contain individual data for every session, and it is possible to use $order or even $query for such global ROUTINE SSVNZA #define global $na(^CacheTemp.ZA($select($isobject($get(%session)):%session.SessionId,1:$job))) fullName(glob) { set global=$$$global for i=4:1:glob set global=$na(@global@(glob(i))) quit global } set(glob, value) public { set global=$$fullName(.glob) set @global=value } get(glob) public { set global=$$fullName(.glob) quit @global } kill(glob, zkill=0) public { set global=$$fullName(.glob) if zkill { zkill @global } else { kill @global } } order(glob, dir) public { set global=$$fullName(.glob) quit $order(@global, dir) } query(glob, dir) public { set global=$$fullName(.glob) set result=$query(@global, dir) quit:result="" "" set newGlobal=$name(^$ZA) for i=$ql($$$global)+1:1:$ql(result) { set newGlobal=$name(@newGlobal@($qs(result,i))) } quit newGlobal } But as I said before, as it is completely undocumented feature a bit difficult to get complete compatibility with ordinary globals. But it still could be useful, in other cases, like it used by InterSystems.
go to post Dmitry Maslennikov · Jun 7, 2016 but WaitMsg it is on parent's side, my control code ususaly like like this set resName="someresource" job ..someJob(jobsData,resName) set child=$zchild for { set $lb(sc,data)=$system.Event.WaitMsg(resName, 1) if sc<=0 { quit:'$data(^$Job(child)) } continue:data="" // do some staff with incomming data }But if you want to interrupt your child process, of course will be possible only from such process, if your process have some loops you may just check if parent still exists, if not, just stop working.
go to post Dmitry Maslennikov · Jun 5, 2016 Stefan had already answered, and you can find his answer here
go to post Dmitry Maslennikov · May 30, 2016 ToPhone(s = "") { set r="" set c=96 for p=2:1:9 { set k="" for j=1:1:3+(p=7)+(p=9) { set k=k_p set c($c($i(c)))=k } } for i=1:1:$l(s) { set n=$g(c($e(s,i)),0) if $e(n)=$e(r,*) set r=r_" " set r=r_n } quit r }
go to post Dmitry Maslennikov · May 30, 2016 and 149, with a little bit changes ToPhone(s = "") { s r="",c=96 f p=2:1:9{s k="" f j=1:1:3+(p=7)+(p=9) s k=k_p,c($c($i(c)))=k} f i=1:1:$l(s){s n=$g(c($e(s,i)),0) s:$e(n)=$e(r,*) r=r_" " s r=r_n} q r }
go to post Dmitry Maslennikov · May 30, 2016 Well, my code below toPhone(s = "") { s r="",c=96,p=1 f i=3,3,3,3,3,4,3,4{s:$i(p) k="" f j=1:1:i s k=k_p,c($c($i(c)))=k} f i=1:1:$l(s){s n=$g(c($e(s,i)),0) s:$e(n)=$e(r,*) r=r_" " s r=r_n} q r } In first I'm generate a plane array with all variants, and then just get all numbers from it, include space between identical numbers, and zero for unknown symbols such as space
go to post Dmitry Maslennikov · May 30, 2016 I think I let other's to share their size, and tomorrow or sometime later I can share it.
go to post Dmitry Maslennikov · May 26, 2016 Actually, XData content it is a just XML, in it is stored exactly as you wrote it in IDE. But it is possible to compile any XData to any other format as you need, at this portal you can find two articles about how to do it, first and second. In your case it could be much easier, like some method which in compile mode gets all XData content and place it to &html<>, which then compile again as usual, and what you need after it is just call this generated method, to show this html.
go to post Dmitry Maslennikov · May 26, 2016 Steve,I would recommend to distant webservers from ECP Application servers. In your case I would install webserver exactly on the server where ECP Application Server have already installed, so in this case your connect via superport will be secured, and in this case only web port should be opened in the firewall. Outside of this servers after firewall you need to use some load balancer, in this case I would recommend to use HAProxy. Connection between extarnal HAProxy and internal webservers could be secured with ssl, but I'm not sure that is really needed.And in this case it is possible to store all static content such as JS, CSS and images on server where load balancer placed, to avoid redundant request to the data servers. It is not a big problem for the production systems.So finally in simple view it my looks like below.
go to post Dmitry Maslennikov · May 18, 2016 It's is not good idea, not all time and not everywhere I have an access to google, but I have an access to my installation, and I have to search on my local machine. And such search should be support browser's search engines. And then I could do something like this.with settingsBut Ensemble documentation, should provide their own search engine to install in my browser without any manual operations. As it possible to do with AddSearchProvider
go to post Dmitry Maslennikov · May 13, 2016 Many times I need to get some link from documentation, to show particular part to someone. And some time it is quite difficult to get such link, and the worst place in Class References, where I can't get good link for a particular part of class.So, what I need is, an active icon with link, which then I can use, and for Class reference too.As an example github.
go to post Dmitry Maslennikov · May 10, 2016 As for me, when I have to investigate some strange behavior in my application, I prefer to read all journal files or limited by time, and then all records, and then I have some some to find exactly what I need, like only kill records or only sets, and only for some concrete global references. So, my usual code looks like this: set exit=0 set docId="14105401" set jrnFile=##class(%SYS.Journal.System).GetCurrentFileName() do { w !!,jrnFile,! set jrn=##class(%SYS.Journal.File).%OpenId(jrnFile) quit:'$isobject(jrn) #dim rec As %SYS.Journal.Record = jrn.FirstRecord set last=jrn.End set pr=0,opr=0 do { set pr=$j(rec.Address/last*100,0,2) if pr'=opr write $c(13),pr_"%" set opr=pr if rec.%IsA("%SYS.Journal.SetKillRecord") { set glb=rec.GlobalNode if $qs(glb, 0),"^My.Global",$qs(glb, 1)=docId { w !!,rec.Address,!,glb w ! } } set rec=rec.Next read q:0 set exit=q="q" quit:exit } while $isobject(rec) quit:exit set sc=jrn.GetPrev(jrnFile, .jrnFile) } while $$$ISOK(sc) quit This code looks all journal files, file by file from latest to oldest, and show progress for current journal file, and shows offset if it found something interesting
go to post Dmitry Maslennikov · May 8, 2016 In my opinion, it's more looks like by mistake, not sure why it could be done in other ways, it still working, with some limitations but any way.We can wait what would say InterSystems, may be they will fix it in future.