Eduard Lebedyuk · Nov 9, 2020 go to post

I'd mimic %DispatchSetMultidimProperty signature. Shame local cannot be casted into a multi-argument call. Wrap in $g if values can be skipped.

/// do ##class().Test()
ClassMethod Test()
{
    kill ^data
    do ..setValue("val", "key1")
    do ..setValue("val", "key1", "key2", "key3", "key4")
    zw ^data
}

ClassMethod setValue(val, args...)
{
    if args=1 {
        set ^data(args(1)) = val
    } elseif args=2 {
        set ^data(args(1), args(2)) = val
    } elseif args=3 {
        set ^data(args(1), args(2), args(3)) = val
    } elseif args=4 {
        set ^data(args(1), args(2), args(3), args(4)) = val
    }
}
Eduard Lebedyuk · Nov 9, 2020 go to post

Terminal works under your OS user, BPL works under InterSystems user (typically irisusr).

This type of error is often caused by:

  1. IRIS OS user does not have permissions to access the input and/or output file
  2. IRIS OS user does not have required executables in PATH
  3. IRIS OS user does not have required shared libraries in LD_LIBRARY_PATH

To solve issue (1) create input and output files in IRIS Temp directory - a path returned by:

write ##class(%File).NormalizeDirectory(##class(%SYS.System).TempDirectory())

To test for issues (2) and (3) add a $zf call which inits all your dependencies and returns version or some other trivia.

To adjust PATH and LD_LIBRARY_PATH use System Management Portal. Here's some info.

I recommend checking environment variables from inside your process with:

write $SYSTEM.Util.GetEnviron(Name)

For quick testing you can adjust environment variables for the current process with this community utiliy.

Eduard Lebedyuk · Nov 8, 2020 go to post

Strange.

Try excluding IRIS directory from antivirus scanning and reinstall the instance.

Eduard Lebedyuk · Nov 6, 2020 go to post

Here's the code to get capitalized piece of string by number:

/// Get Capitalized piece.
///   str - string to search
///   piece - 1-based piece to return
/// write ##class(test.CustomQuery).GetCapitalizedPiece()
ClassMethod GetCapitalizedPiece(str As %String = "lowerHelloWorldAgainHelloWorldAgainHelloWorldAgainHelloWorldAgain", piece As %Integer = 1) As %String
{
    
    // Only uppercase letters
    set upper = $tr(str, $zcvt(str, "l"))
    
    // Number of uppercase letteres
    set capCount = $l(upper)
    
    // Quit if requested piece can not exist
    quit:piece>capCount ""
    quit:piece<1 ""
    
    // First letter in our capitalized piece
    set startLetter = $e(upper, piece)
    
    // All previous capitalized letters
    set prevLetters = $e(upper, 1, piece - 1)

    // Check if current capitalized letter is a first capitalized letter
    // If not - skip previous capitalized letters
    if '$find(prevLetters, startLetter) {
        set start = $find(str, startLetter) - 1
    } else {
        set count = $l(prevLetters, startLetter) + 1
        set start = 0
        while $i(count,-1) {
            set start = $find(str, startLetter, start)
        }
        set start = start - 1
    }
    
    // Is this capitalized piece the last one?
    if piece=capCount {
        set end = $l(str)
    } else {
        set end = $find(str, $e(upper, piece + 1), start) - 2
    }

    quit $e(str, start, end)
}

Advantages over regexp:

  • ~50 times faster
  • Get any piece, not only the second one (although generating required regexp by piece number seems easy to do)
Eduard Lebedyuk · Nov 6, 2020 go to post

OS user must have Windows registry write access to edit server connections.

Try adding a server as a local Administrator user.

Eduard Lebedyuk · Nov 4, 2020 go to post

4 years later I can safely say that Poolsize can exceed the number of CPU Cores.

The main exception is when a job consumes the entire CPU core. Usually a process mostly waits for network, disk io or some other io or whatever. In that case it's perfectly fine for Poolsize to exceed available CPU Cores - let them wait together. On the other hand if a process consumes a CPU core entirely - in that case Poolsize exceeding CPU cores count won't help.

In general increasing Poolsize overly much can lead to overconsumption of CPU or disk or other resources, which depending on resource concurrency model may (and often does) negatively impact overall performance.

To @alex chang I can recommend monitoring queues count, sql queries, system metrics and cpu/ram/hdd consumption - clearly there's a bottleneck somewhere.

Additionally while Visual Trace is a great tool it does not show everything a process does - only messages sent and received. Is there anything a process does between receiving a request and calling another process?

Finally Monlbl can be used to check which part of the generated BPL code takes a lot of time.

Eduard Lebedyuk · Nov 4, 2020 go to post

IIRC you're doing Tesseract OCR - in that case I would recommend the following architecture:

  • %CSP.REST handler to get requests
  • empty BS to proxy requests
  • BO (PEX or otherwise) to interact with Tesseract  (in goes binary stream or a file name out goes text stream)
Eduard Lebedyuk · Nov 4, 2020 go to post

It does, but %CSP.REST is a preferable way of handling REST Requests.

As you can easily call production from REST just do it whenever the need arises.

Eduard Lebedyuk · Nov 3, 2020 go to post

Here's how you can create a REST API and call production from it.

Inside your REST handler you can access request body via:

  • %request.Data - get access to request body as one binary or character stream
  • %request.GetMimeData("MimeDataName") - get access to request form data value as binary or character stream

Here's a simple fileserver in ObjectScript.

Eduard Lebedyuk · Nov 1, 2020 go to post

In Docker it's as easy as

ENV GWDIR /jgw
ENV ISC_PACKAGE_INSTALLDIR /usr/irissys
ENV ISC_JARDIR $ISC_PACKAGE_INSTALLDIR/dev/java/lib/JDK18

COPY --from=store/intersystems/iris-community:2020.2.0.211.0 \
     $ISC_JARDIR/intersystems-gateway-3.1.0.jar \
     $ISC_JARDIR/intersystems-jdbc-3.1.0.jar \
     $ISC_JARDIR/intersystems-utils-3.1.0.jar \
     $ISC_PACKAGE_INSTALLDIR/dev/java/lib/gson/gson-2.8.5.jar \
     $GWDIR/

You can check this article for more details.

Eduard Lebedyuk · Oct 28, 2020 go to post

I think you need a proxy method in InterSystems IRIS to accept one argument - new path and return the status.

You need to call Modify method of Config.Journal class, but it has one argument which is a local and currently locals are not supported by Native API IIRC.

Calling @Bob Kuszewski

Eduard Lebedyuk · Oct 28, 2020 go to post

One new idea for contestants:

MLOperation. Currently, PythonGateway provides low-level PythonOperation aimed at expert users who write Python code themselves. The idea of MLOperation is to build a high-level Interoperability adapter targeted at a broader userbase. Essentially your adapter provides generalized Fit/Predict/Optimize methods and users need to provide the data, target model type, and hyper parameter values. This closes the gap between the hands-off approach of the IntegratedML and low-level approach of the PythonGateway. The work is described in this issue.

Eduard Lebedyuk · Oct 27, 2020 go to post

Some ideas for contestants:

  • IMAP - while InterSystems IRIS supports SMTP/POP3 protocols native support for IMAP would be an interesting addition. Discussion.
  • Swagger BO - Swagger is a leading REST API specification format. Automatically generate Business Operation from swagger specification
  • BS for binary protocols - similar to Swagger above but for binary protocols. Write-up is here.
Eduard Lebedyuk · Oct 25, 2020 go to post

Try this

Class test.SQL
{

ClassMethod GetMsg(length As %Integer) As %Stream.TmpCharacter [ SqlProc ]
{
    Set stream = ##class(%Stream.TmpCharacter).%New()
    Set chunkLength = 32000
    Set chunk = $tr($j("", chunkLength)," ", "A")
    
    If length>=chunkLength {
        For i=1:chunkLength:length {
            Do stream.Write(chunk)
        }
    }
    
    Set tailLength = length#chunkLength
    Do:tailLength>0 stream.Write($e(chunk, 1, tailLength))
    
    Set sc = stream.%Save()

    Quit stream  //."%%OID" <- also works for persisted streams
}
}

Worked for me with this SQL (in SMP):

SELECT test.SQL_GetMsg(10), test.SQL_GetMsg(1)
UNION
SELECT test.SQL_GetMsg(10), test.SQL_GetMsg(2)  
Eduard Lebedyuk · Oct 21, 2020 go to post

i.e. do not place propertynames under quote

Property Arrival Time has a whitespace in the name so it must be quoted.

Eduard Lebedyuk · Oct 20, 2020 go to post

As it looks like a security issue I would recommend a slightly different approach.

1. If you do not have the class for your XML, create it from XSD or manually.

2. Convert your XML string into an object of class (1).

3A. If you  want just to skip some properties (like RollNo) set availability of these projected properties to IN - this way property is used by import but is ignored on export. Alternatively disable projection of this property altogether.

3B. If you really want to return *** from your value (what's the use case?) add a new datatype test.PrivateString and use it to store RollNo value - during OBJ->XML projection it would be exported as ***.

Class test.PrivateString Extends %String
{

/// Declares the XSD type used when projecting XML Schemas.
Parameter XSDTYPE = "string";

/// Return "***"
ClassMethod LogicalToXSD(%val As %TimeStamp) As %String [ CodeMode = generator, ServerOnly = 1 ]
{
    If ($$$getClassType(%class)=$$$cCLASSCLASSTYPEDATATYPE) || $$$comMemberKeyGet(%class,$$$cCLASSparameter,"XMLENABLED",$$$cPARAMdefault) {
        Set %codemode=$$$cMETHCODEMODEEXPRESSION
        Set %code="""***"""
    } Else {
        Set %code=0
    }
    Quit $$$OK
}

}