go to post Alexey Maslov · Apr 19, 2017 Hello Eduard,Following your 1st link I've failed to find any info on 7-zip usage. It was about Libre Office stuff.No problems with 7-zip for Windows (its native OS), not worth to mention that 7z format compression method(s) of its last versions can be unsupported (and uncompressed) with old ones. If using zip format (even with 7-zip), your are quite safe.p7zip, its Linux branch, which I tried several years ago, performed very poor as it was not multi-threaded (as 7-zip was for years). So we decided to drop it mostly for this reason.One little hint about 7-zip for Windows: we use its 7za.exe build in our deployment and update procedures as it doesn't need installation and can be just dropped in any place before usage.
go to post Alexey Maslov · Apr 14, 2017 Looks like, do you use integration in Studio?Yes, I do....maybe it working so long, and you did not wait for the result. Not too long, just a few seconds. It provided an empty report, and AFAIR there was some record in its internal log that no problems were found.
go to post Alexey Maslov · Apr 14, 2017 I've tried it with Cache 2017.1.0. The first impressions are:- It completely ignored all "blockers" in the class with 300+ methods, while there was a many of them it should not pass by: 'not procedureblock', legacy flow control, etc; maybe some internal limit was exceeded. - On the smaller class it popped rather funny messages, e.g. 1 blocker 1 Method declared as "ProcedureBlock = 0" User.test.cls(+6): ClassMethod RestVars(zzzzzzsource As %String, bOref As %Boolean) As %Status [ ProcedureBlock = 0 ] 2 major 2 Usage of QUIT to exit a method User.test.cls(+24): quit $$$OK User.test.cls(+28): quit $$$ERROR($$$CacheError, $ze) Well, 'ProcedureBlock = 0' can be considered not a good style, but what if I need it in some special case? This very method restores local variables stored in a global, so it would loose all its sense be it 'procedureblock'. IMHO, there should be a facility to protect such special case methods. 'Usage of QUIT to exit a method': QUIT is standardized command to exit methods/routines/etc. IMHO it's better than RETURN as it prevents coders to exit their methods from inside the loops and try/catches constructs, so it encourages the modular coding: each module (method) should have one enter and one exit. So, many of such rules seem to be subjective. Without a facility to customize the rules this linter seems to be no more than a demo of some commercial product. P.S. As I noticed later, it never clears 'mgr/Temp/' sub-folders it creates.
go to post Alexey Maslov · Apr 14, 2017 I just wanted to add that some occurrences of `quit:$quit value quit` idiom was probably inherited from legacy coding practices, as Caché allows calling methods/functions using DO command (thus ignoring the value being returned).From the other hand, using of `quit:$quit "" quit` construct looks like an attempt to amend erroneous $$ calls of methods/routines, which did not return any value by their initial design. One might say that such design was initially wrong, as any method should return something, at least %Status which is always $$$OK.
go to post Alexey Maslov · Apr 13, 2017 It may depend on local coding traditions. Just for example, quick search using Studio's <Ctrl-F> through one of our apps (comprised 1000+ classes) showed: q:$q <lvn> q ; Found 4501 occurrence/s in 90 file/s quit:$quit <lvn> quit ; Found 126 occurrence/s in 28 file/s. if $quit ; Not found. So, it turned that about 10% of our classes used $QUIT. Not too many, but not negligible few.
go to post Alexey Maslov · Apr 13, 2017 My point was if the compiler will go as far as protecting the developer from this type of quit misshap It seems that this kind of checkup would be difficult to implement because of variety of methods how the code could be branched depending on its call type. E.g. if $quit { ... quit rc } else { ... quit } or quit:$quit rc ... ... quit Of course, both code fragments demonstrate not very good coding style, but they are semantically correct. If one gets many false positives from a (hypothetical) code checker, he would likely drop it.
go to post Alexey Maslov · Apr 13, 2017 ... studio will complain when you try and quit a for loop with a valueQUIT from inside a loop is considered quitting a loop rather then a function, so it should always be without a value....but it wont complain when you try and quit with nothing on the methodAccording to language definition, each function can be called as a function (set x=$$function(...)) or as a routine (do function(...)). The call type can be recognized inside the function using a construct: quit:$quit ReturnValue quit ; $quit=1 if called as a $$function()Methods are compiled to functions and behave the same way.
go to post Alexey Maslov · Apr 6, 2017 Thank you, Ray.Most production sites wouldn't plan their backups this way because it means that the only operation you can do on the backup image is restore the whole thing and start Caché.Another reason of doing so can be the number of articles, docs, learning materials which taught us _always_ perform ExternalFreeze accompanied with ExternalThaw on every external snapshot making.
go to post Alexey Maslov · Apr 5, 2017 support staff can't access the System Management PortalYour support staff may want to keep one open session with SMP for troubleshooting.
go to post Alexey Maslov · Apr 5, 2017 crash-consistent snapshot of the entire system, including all CACHE.DAT files, the manager directory, journals, and the WIJ.Ray, may I ask you to clarify this a bit?Is any snapshot of the entire system can be considered crash-consistent?
go to post Alexey Maslov · Apr 4, 2017 if you need to start an ECP data server in a mode that prevents application servers from connecting to itFor such cases we implemented a facility which stops all users' sessions and disables new logons ECP-wide (on application level). Our aim was to prevent extra Caché restarts on data servers by any price, as each restart means loosing the global cache and slowing down of many app functions for ~1 hour or even longer.Without this facility maybe I'd choose to temporarily stop %Service_ECP.
go to post Alexey Maslov · Apr 3, 2017 RTFM, should I say to myself. <Database> tag is required, according to documentation: <Database> Required; within <Configuration>. Defines one or more databases used in the namespace. Taking in account that there was no need in any database [re]configuration, the problematic fragment was scaled down to: <!-- Configure the database that should exist upto this step: we don't need it, but the tag is required --> <Database Name="${MainDB}" Create="no" /> And it was enough to make it working. Despite of nasty message logged: 2017-04-03 12:32:18 1 CreateDatabase: Creating database USER1 in D:\InterSystems\Cache10\mgr\ with resource nothing bad happened neither with CACHESYS, nor with USER1 databases.
go to post Alexey Maslov · Apr 2, 2017 only need to delete all created filesGood point. Besides, if there was <Configuration> section involved, you need to revert all configuration changes it had done. And it's not as easy as rename previous cache.cpfXXXXX back to cache.cpf, because %Installer commits every single change, so you need to remember which cache.cpfXXXXX was active before you started your experiments.It seems it's usually easier to remove all changes made by %Installer manually, using SMP and some File Manager tool, or if there are too many of them, just run the setup being tested against another Cache instance which I can easily remove and reinstall from scratch.
go to post Alexey Maslov · Apr 1, 2017 Thanks for taking part, Eduard. According to this record: 2017-03-31 19:24:51 0 : USER1 is ready to be configured using USER1 and %DB_%DEFAULT MainDB was neither undefined nor empty. My post was mainly about %Installer's usage; doesn't it use transactions internally by itself? I didn't notice any transaction tags in it's language definition. As to the log level, my other scripts (not this one) are intended to be used during Cache installation. I don't know how to adjust the log level in this case, therefore I used to deliberately set it equal to "0" to be sure that my debug messages will be logged; having finished debugging, I just change all Level="0" to Level="3"
go to post Alexey Maslov · Apr 1, 2017 Don't you think that naming the file "1.xml.gz" would be more clear?
go to post Alexey Maslov · Apr 1, 2017 Thanks for taking part, Eduard. According to this record: 2017-03-31 19:24:51 0 : USER1 is ready to be configured using USER1 and %DB_%DEFAULT MainDB was neither undefined nor empty. My post was mainly about %Installer's usage; doesn't it use transactions internally by itself? I didn't notice any transaction tags in it's language definition. As to the log level, my other scripts (not this one) are intended to be used during Cache installation. I don't know how to adjust the log level in this case, therefore I used to deliberately set it equal to "0" to be sure that my debug messages will be logged; having been finished with debugging, I just change all Level="0" to Level="3"
go to post Alexey Maslov · Mar 31, 2017 I've got into another issue trying to write a code which should configure some global mapping to already existing namespace. Here is its skeleton: Include %occInclude /// Create simple global mapping (main DB and cachetemp only) Class %z.Mapping Extends %Library.RegisteredObject { /// Application Definition XData qMS [ XMLNamespace = INSTALLER ] { <Manifest> <If Condition='$L("${NAMESPACE}")=0'> <!-- Report an error if the namespace wasn't specified --> <Error Status="$$$NamespaceDoesNotExist"> <Arg Value="${NAMESPACE}"/> </Error> </If> <If Condition='$zcvt("${NAMESPACE}","U")="%SYS"'> <!-- Report an error if the namespace = %SYS --> <Error Status="$$$GeneralError"> <Arg Value="${NAMESPACE} should not be %SYS"/> </Error> </If> <Invoke Class="%z.Mapping" Method="GetMainDB" CheckStatus="0" Return="MainDB" > <Arg Value="${NAMESPACE}"/> </Invoke> <If Condition='$L("${MainDB}")=0'> <!-- Report an error if the globals database was not detected --> <Error Status="$$$GeneralError"> <Arg Value="${NAMESPACE} has no Globals database" /> </Error> </If> <Var Name="DBRESOURCE" Value="%DB_%DEFAULT"/> <Namespace Name="${NAMESPACE}" Create="no" > <Log Level="0" Text="${NAMESPACE} is ready to be configured using ${MainDB} and ${DBRESOURCE}" /> <Configuration> <!-- ??? Configure the database that should exist upto this step --> <Database Name="${MainDB}" Dir="${MainDB}" Create="no" Resource="${DBRESOURCE}" PublicPermissions="" /> <!-- ??? Configure the database that should exist upto this step --> <GlobalMapping Global="Q("0!"):(END)" From="CACHETEMP"/> <GlobalMapping Global="Q("0mseSTATUS"):("0mseSTATUS~")" From="${MainDB}"/> <GlobalMapping Global="Qa" From="CACHETEMP"/> <GlobalMapping Global="Qi" From="CACHETEMP"/> <GlobalMapping Global="Qi("MO")" From="${MainDB}"/> </Configuration> </Namespace> </Manifest> } /// This is a method generator whose code is generated by XGL. ClassMethod setup(ByRef pVars, pLogLevel As %Integer, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ] { #; Let our XGL document generate code for this method. Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "qMS") } ClassMethod GetMainDB(pNamespace) As %String { new $namespace s $namespace="%SYS" s sc=##class(Config.Namespaces).Get(pNamespace,.p) if 'sc d $system.OBJ.DisplayError(sc) set ret="" g gmdbq s ret=p("Globals") gmdbq quit ret } /// Invoke the installer passing in some variables /// zn "USER1" s sc=$system.OBJ.Load("mapping.xml","ck") d:'sc $system.Status.DisplayError(sc) /// s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc) /// ClassMethod RunInstallWithLog(pLogfile As %String, pLogLevel = 1) As %Status { #dim tVars #dim tStatus As %Status Set tVars("NAMESPACE") = $zu(5) // Construct a file logger #dim tLogger As %Installer.FileLogger = ##class(%Installer.FileLogger).%New(1,pLogfile) // Invoke the installer Set tStatus = ..setup(.tVars,pLogLevel,,tLogger) Do:$$$ISERR(tStatus) $system.OBJ.DisplayError(tStatus) Quit tStatus } } I've started it at the same Cache instance where I wrote it with following commands: zn "USER1" s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc) And it works. But if I'm excluding an [unneeded] code fragment that resides between two lines: <!-- ??? Configure the database that should exist upto this step --> I'm getting several errors, please see below. Why this fragment is still needed nevertheless I marked my namespace with Create="no" attribute? 2017-03-31 19:24:51 0 %z.Mapping: Installation starting at 2017-03-31 19:24:51, LogLevel=2 2017-03-31 19:24:51 0 : USER1 is ready to be configured using USER1 and %DB_%DEFAULT 2017-03-31 19:24:51 1 CreateNamespace: Creating namespace USER1 using / 2017-03-31 19:24:51 2 CreateNamespace: Modifying namespace USER1 2017-03-31 19:24:51 0 %z.Mapping: ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","") ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required ERROR #7202: Datatype value '' length less than MINLEN allowed of 1 ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required ERROR #7202: Datatype value '' length less than MINLEN allowed of 1 2017-03-31 19:24:51 0 %z.Mapping: ERROR #ConfigFailed: Unknown status code: <Ins>ConfigFailed (,,,,,,,) > ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","") > ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1 > ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1 2017-03-31 19:24:51 0 %z.Mapping: Installation failed at 2017-03-31 19:24:51 2017-03-31 19:24:51 0 %Installer: Elapsed time .178622s
go to post Alexey Maslov · Mar 28, 2017 Timothy, Both options succeded. Thank you so much! Just FYI, your 1st option <Var Name="AddClassesErrors" Value="",5202,5373,"" /> generated a code: Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""") and the 2nd one <Var Name="AddClassesErrors" Value="#{"5202,5373"}" /> generated: Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))
go to post Alexey Maslov · Mar 28, 2017 Timothy, Both options succeded. Thank you so much! Just FYI, your 1st option <Var Name="AddClassesErrors" Value="",5202,5373,"" /> generated a code: Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""") and the 2nd one <Var Name="AddClassesErrors" Value="#{"5202,5373"}" /> generated: Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))
go to post Alexey Maslov · Mar 28, 2017 Hi Timothy and Sergey, Timothy, putting the literal codes list inside a variable doesn't help much: I'm getting the same error. Thank you anyway. P.S. The reason of the issue is a wrong code for variable evaluation, e.g. <Var Name="AddClassesErrors" Value="5202,5373" /> is transformed to Do tInstaller.SetVariable("AddClassesErrors","5202","5373")