Thank you, John, for this reminder, while I prefer to deliberately enable auditing of all events for the simple reason: a few added-on disk IOPS, as well as 1GB+ of disk space for audit database (even on high loaded Caché instances) do not seem to be a great price for an ability to trace not predicted cases in production.

We even met a prospect's requirement that any audit database write failure should be dealt as a critical error with a notification of admins by all means, even with shutting the Caché down!

... and second thing is to insert a Close command before the correspondent Open, e.g.

..c resfile o resfile:"NWK\CP874\"

even if you are quite sure that the file is closed at the moment of opening. The reason is to avoid the cases when your program have failed with error without closing the file, so its open parameters (at least, the translation table setting) keep unchanged despite of subsequent Open command. Such cases often happen during development / debugging, when the error trap code is completely switched off or greatly simplified.

It's better to use stream wrappers instead of open/use directly

Just because of higher abstraction level of streams; nothing special inside: just the same Open/Use/etc COS commands. So, choosing one or another way of working with files can't be the source of errors itself.

By the way, `open file:("NWK\table\")` is a proven and documented way of translation table setting. AFAIK, its only limitation is that it works with Open command only rather than with Use command.

Eduard, can you explain the main difference between Sagun's method of translation table setting:

 open file:"RSK\CP874\"

and yours in this very case? The latter is a piece of code of  %Stream.FileCharacter.cls which actually sets a table:

 If (table'=0) && (i%CurrFile'="") {
   Set io=$io
   Use i%CurrFile
   $$$SETIO(table) ; -> Do $zutil(96,18,2,table)
   Use io
 }

It seems that it's some other problem, perhaps a bug.

Sagun, if you provide us with a small piece of your code where you open the file, use it, and write it, it would be easier to say something.

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.

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.

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.

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.

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.

... studio will complain when you try and quit a for loop with a value

QUIT 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 method

According 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.

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.

if you need to start an ECP data server in a mode that prevents application servers from connecting to it

For 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.

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.

only need to delete all created files

Good 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.

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"