The behavior you are seeing is because of chain '.' handling of null objects. For example if you:

Set person=##class(Sample.Person).%New()

Write person.Name.AnythingYouLike

It will succeed and return "", but if you:

Set tmp=person.Name

Write tmp.AnythingYouLike

It will fail with an INVALID OREF error, as would 'Write (person.Name).AnythingYouLike'.

This behavior is inconsistent, so I will not defend it, but it is how the product works.

Streams support the idea of writing to them without changing the previous stream content so you can either accept the newly changed stream value or discard it depending on if you call %Save or not. In order to support this when you attach to an existing file and then append some data you are actually making a copy of the original file and appending data to this copy. When you %Save this we remove the original file and rename this copy so this is now the version you see. However as you can see making a copy of a file is a potentially expensive operation especially when the file gets large so using a stream here is probably not what you want.

As you just want to append the data and do not want file copies made I would just open the file directly in append mode (using either 'Open' command directly or %File class) and write the data you wish to append so avoiding stream behavior.

Can you provide the code you are using currently so we have something definitive to base comments off, but have you tried using $translate and reading the data in big chunks e.g. 16k at a time?

While 'binarystream.AtEnd {

  Set sc=outputstream.Write($translate(binarystream.Read(16000),badchars,goodchars)

}

Where binarystream is your binary input stream, outputstream is your output stream with the converted characters and badchars is a list of the bad characters you need to convert and goodchars is the list of the values you want the badchars converted into.

The biggest issue I saw is that when you call %Save() you are returning the Status code into variable 'Status' which is good, but then this variable is totally ignored. So if you save an object which does not for example pass datatype validation the %Save will return an error in the Status variable but the caller will never know the save failed or why the save failed.

In addition %DeleteId does not return an oref, it returns a %Status code, so you need to check this to see if it is reporting an error and report this to the caller if it does also.

The key bit of information here is the 'service unavailable' error being returned rather than say a 'not authorized' or 'not found' errors. By default when out of licenses we return the service unavailable error so if anyone else sees this they should check license usage as a first step. If you get not authorized errors it is probably a security issue so check the audit log as this often shows the exact problem.

It sounds like somewhere in your application you have a call that returns OID values to the client, then as a separate step you wish to return the stream associated with this OID. Is it possible instead of returning the OID to the client you just return the stream directly to the client? So what is the need for the client to store the OID when it is really the stream the client wants?

Assuming there is a good reason for returning the OID you can follow this pattern.

  • Server gets request where it would previously return the OID
  • Server generates a new random number using $system.Encryption.GenCryptRand to generate a random number
  • Server stores this random number in a table against the OID it wishes to associate it with and a timetstamp
  • Server sends the random number to the client
  • Client at some point wishes to get the stream so it sends the random number to the server
  • Server looks up the random number in the table and finds the OID and serves up this stream if the request is within some time period of the random number being generated. Then it deletes the random number from the table.

You also need to write some code to cleanup this table and remove expired random numbers from the table periodically or it could grow over time if you generate values and the client never uses them.

As you know these escape sequences are valid HTML escaping of unicode characters. The general principal is always that you store the text in the database  as characters i.e. not escaped at all and you apply any escaping needed when serving this content to a client. So it appears you need to convert these escaped characters into something you can store in your 8bit database.

Now in general I would suggest using unicode in which case you can just make sure the data being sent to you is correctly converted into unicode characters and then you just store the characters in the database. This would then work with any characters and not just the few you are having problems with. However it sounds like you do not want to move from 8bit to unicode. If that is the case anything you do will be something of a hack, but you can just use $replace on the data coming in to convert say "%u2019" to "'" before you store it in the database to 'normalize' the input. This solution will only cover a few characters where you can find a suitable replacement but it may be enough to get by for the short term while you investigate moving to unicode as a permanent solution.

In Cache 2017.1 we have light weight SQL profiling enabled by default which will keep track of the number of times each SQL query is called and how long these queries take.  So you can quickly answer the question as to which SQL queries matter to your application and then investigate these important queries in more detail.

This information will show up in the system management portal in the SQL explorer section under the sql queries in this namespace section.

Some tools do not like the fact that the RSS feed is badly formatted as there are no title fields, this was reported a long time ago but is still not fixed for example the start of 'https://community.intersystems.com/group/8046/feed':

<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xml:base="https://community.intersystems.com/group/8046" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:og="http://ogp.me/ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:sioc="http://rdfs.org/sioc/ns#" xmlns:sioct="http://rdfs.org/sioc/types#" xmlns:skos="http://www.w3.org/2004/02/skos/core#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <channel>
    <title></title>
    <link>https://community.intersystems.com/group/8046</link>
    <description></description>
    <language>en</language>

Why do you want the connection id to change? In CSP we route multiple requests to a few server processes to be able to handle massive numbers of client efficiently. However the process that handles the request does not hold any information about the session at all, all this information such as the license we hold is in the %session object so as long as you have a new %session object this is a brand new connection.

I think we may need some more context of what the code looks like and exactly what error message you are getting. We do support $$$CacheError in 2016.1, for example:

Set status=$$$ERROR($$$CacheError,$zerror)

If you are trying to lookup the macro value then use $$$ERRORCODE($$$CacheError), then you can write logic like:

If errorcode=$$$ERRORCODE($$$CacheError) Write "It was a Cache error",!