For a string like "hallo" Cache will use 5+2 = 7 bytes. If that "hallo..." is longer then 253 bytes then length_of_string + 4 bytes will be used and if your "hallo..." is longer then 65535 bytes then length_of_string + 6 bytes will be used.

But there is one more thing, you should know: the sum of the lengths of ALL properties, except the array(like) properties, can't be greater then that famous 3641144 magic number (if you use the standard Cache Storage). Array-like properties are those, which are stored in own nodes.

The documentation of the %ToJSON() method is correct and yes, you can do 

 do obj.%ToJSON()
 

merely, this works only "on devices without protocol" like terminal, (sequential) file, etc. Everywhere, wehere the data bytes goes direct to the target. WebSocket isn't such a device. There is a "header part", with information fields like the command, masking, the length of the data, etc.

You have two possibilities, a) you ask WRC for a "WriteStream()" method or b) you handle the whole WebSocket by hand (is not impossible) or c) you change your application logic and send the those messages in chunks.

Yes, it would

kill
set $p(data,"x",2E6+1)=""
set dynArr=[].%Push(data).%Push(data).%Push(data)
set stream=##class(%Stream.GlobalCharacter).%New()
do dynArr.%ToJSON(stream)
try { set x=dynArr.%ToJSON() } catch e { write e.Name,! }
write stream.Size,!
write $length(data),!

The output is

<MAXSTRING>
6000010
2000000

As Steven Hobbs  wrote, the limit is only set by the size of the (virtual) memory.

I have nothing in common with HealtShare... but a property (Property rsXML As %String), like you mentioned, can hold up to 3.6E6 chars. If your data are longer then 3641144 characters then you definitely need to use streams (and if you intend to Base64 encode the data, then your data will be 33% longer)! You need to change the above property to something like

Property rsXML As %GlobalBinaryStream;

BUT this means, you have to adapt all your programs, methods, etc. where this property is in use!

That won't be a joy rather a pain

what you need is a SIMPLE compare of two strings

Set Ret=RSet.Execute()
set currentTS = $zdt($h,3)    // get onece the current timestamp
 While RSet.Next()
  { Set routeGuid="" 
Set nextScheduled=RSet.GetData(9)
//I $ZDATETIME($h,3,1)>nextScheduled S ^badis("datetime",Id)=$ZDATETIME($h,3,1)_"|"_nextScheduled
if currentTS ] nextScheduled S ^badis("datetime",Id)=currentTS_"|"_nextScheduled
}

In words: if currentTS follows (i.e. greater) nextScheduled - that's all.

If you can't find the methods Vitaliy mentioned because (for example) your Cache version is too old, you can always reinvent the wheel and write your own encoder/decoder method. Where is the problem? This snippet could be a starting point

Parameter Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

ClassMethod Base64Enc(x)
{
    s f=-$l(x)#3, x=x_$e($c(0,0),1,f), y=..#Base64, z="" zt:$ziswide(x) "WIDE"
    f i=1:3:$l(x) s a=$a(x,i)*256+$a(x,i+1)*256+$a(x,i+2), c=262144 f j=1:1:4 s z=z_$e(y,a\c+1), a=a#c, c=c\64
    s:f z=$e(z,1,$l(z)-f)_$e("==",1,f) q z
}