Julian Matthews · Jul 18, 2024 go to post

Thanks Scott.

I'm also not rushing to delete based on counts, but it's still interesting to review.

I ran the "Complete Ensemble Message Body Report" from Suriya's post's Gist against a namespace and it ran for about 8 hours, which now has me nervous to run the Delete option. Although, to be fair, this is a namespace that has been in operation for about 10 years, so I might start smaller and work my way up.

Julian Matthews · Jul 16, 2024 go to post

The key difference between the two scenarios is that you're running the application for the instances where you are getting the correct time.

Have you checked the server timezone settings for the user running Ensemble in your environment?

Julian Matthews · Jul 15, 2024 go to post

This feels like a timezone/locale issue - is the difference between your time zone and UTC also 5 hours apart?

Julian Matthews · Jul 4, 2024 go to post

Hi Joshua.

Is it possible that there is a group policy in place that is being applied to you and not your colleagues? Have you tried forcing an update of your group policies applied to your profile - from the windows terminal/command line:

gpupdate /force

Alternatively, do you have any extensions installed in Edge that you don't have installed for Chrome? Maybe an adblocker?

Finally, have you tried opening devtools on this page, refreshing, and then seeing if there are any meaningful errors appearing under the Console or Network tab?

Julian Matthews · Jul 2, 2024 go to post

The only way I can think of doing this would be to split out the Helper() ClassMethod into its own class, and then ensure the order of compilation is in such a way that the class containing the Helper class method is compiled. Depending on how you're achieving this, could you use the CompileAfter class keyword?

So something like:

Class 1:

Class Foo.Bar.1
{

ClassMethod Helper()
{
// do something
}

}

Class 2:

Class Foo.Bar.2 [ CompileAfter = (Foo.Bar.1) ]
{

ClassMethod Generated() [ CodeMode = objectgenerator ]
{
do ##Class(Foo.Bar.1).Helper()
// do something else
}

}
Julian Matthews · Jun 14, 2024 go to post

There's a good thread from a few years ago that goes into various ways of converting date formats which you can find here.

My approach in that thread was to suggest the use of the class method ##class(Ens.Util.Time).ConvertDateTime()

In your case using this option, you could do this for your main question:

Set Output = ##class(Ens.Util.Time).ConvertDateTime(input,"%Y-%m-%d %H:%M:%S.%N","%Y%m%d")

And then for your followup to include seconds:

Set Output = ##class(Ens.Util.Time).ConvertDateTime(input,"%Y-%m-%d %H:%M:%S.%N","%Y%m%d%H%M%S")

Julian Matthews · May 21, 2024 go to post

If I were to write this in an operation using the EnsLib.HTTP.OutboundAdapter, my approach would be something similar to:


	Set tSC = ..Adapter.SendFormData(.webresponse,"GET",webrequest)

	//begin backoff algorithm
	
	//Get start time in seconds
	Set startInSeconds = $ZDTH($H,-2)
	
	//Set initial params for algorithm
	Set wait = 1, maximumBackoff=64, deadline=300
	
	//Only run while Status Code is 504
	While (webresponse.StatusCode = "504"){
		
		//HANG for x.xx seconds
		HANG wait_"."_$RANDOM(9)_$RANDOM(9)
		
		//Call endpoint
		Set tSC = ..Adapter.SendFormData(.webresponse,"GET",webrequest)
		
		//Increment potential wait periods
		If wait < maximumBackoff Set wait = wait*2

		//Adjust wait if previous action takes us above the maximum backoff
		If wait > maximumBackoff Set wait = maximumBackoff
		
		//Check if deadline has been hit, exiting the While loop if we have
		Set currentTimeInSeconds = $ZDTH($H,-2)
		If (currentTimeInSeconds-startInSeconds>=deadline){Quit}
	}

This is untested however, so massive pinch of salt is required 😅

Julian Matthews · May 8, 2024 go to post

I'm not sure if it's related, but my colleagues and I all notice random performance issues with the management portal when accessing our non-production environment that's using IIS.

It was deployed to our non-production environment for similar experimentation reasons, but I never took it further due to these issues (and it dropped from my radar due to still being on 2022.1.2 with no immediate pressures to upgrade)
I need to upgrade the version of web gateway following a recent email from Intersystems, so I'm going to run that now and then reboot the machine and see if I see any changes.

Beyond that, I'm going to be following this discussion closely to see if our issues are related and if there is a solution.

Julian Matthews · Apr 25, 2024 go to post

Hi Mary.

If you did want to create your own method to do this, you could do something like this:

Class Demo.StopThings
{

/// Stops all services in the specified production
ClassMethod StopAllServices(ProductionName As %String) As %Status
{
    Set sc = $$$OK, currService=""
    Set rs = ##class(Ens.Config.Production).EnumerateConfigItemsFunc(ProductionName, 1)
    While rs.%Next() {
        Set currService = rs.Get("ConfigName")
        Write "Stopping the following service: "_currService_"..."
        Set tSC = ##class(Ens.Director).EnableConfigItem(currService, 0)
        If tSC = $$$OK{Write "Complete!",!}
        Else{Write "Failed",!}
        }
    Return sc
}

}

And then you could call it from terminal from relevant namespace by running:

Set Status = ##class(Demo.StopThings).StopAllServices("Insert.ProductionName.Here")

To use the same code for Operations, change the 1 to a 3 in the call to "EnumerateConfigItemsFunc" (and swap out the bits that say service for operation).

The above is some quick and dirty code to demonstrate the approach you could go for, but you may want to add in some error handling etc to make things more robust given your usecase.

Julian Matthews · Apr 18, 2024 go to post

Thanks Nick.

Having done some tests (and reading the link shared), the table locking still allows SELECT statements to be executed so the objectscript locking will be my approach.

Julian Matthews · Apr 16, 2024 go to post

Hey Scott.

I saw this post when you originally made it, and I was curious to know what direction you went with?

Julian Matthews · Apr 9, 2024 go to post

Thanks for sharing this Neil - I hadn't considered the benefits of mapping the credentials and the subsequent SecondaryData, and is an approach I will be looking at going forward.

Julian Matthews · Apr 5, 2024 go to post

As a short term approach, you may want to look into using Stunnel in client mode to encrypt the traffic and then set something up similar to:

This would mean that the traffic between your 2016 instance and stunnel is unencrypted but all on the same machine, and then stunnel handles the encryption between your machine and the external site using TLS1.3.

However, even if you go this route, I would still recommend getting the process started for upgrading to a newer version.

Julian Matthews · Feb 2, 2024 go to post

Increasing the pool value will have some effect on the RAM and CPU usage, but no different than having other jobs running through the production. If you move all the components over to using the actor pool (by setting the individual pool settings to 0) it should be easy enough to raise the value bit by bit while keeping an eye on the performance of the production and the cpu/ram usage of the machine to find a sweet spot.

If the API just needs a bit of extra resource when there's a small spike in the inbound requests, then this should not be of too much concern as it will just calm down once it's processed what has been requested.

If, however, there's a chance that it could be overloaded from inbound requests and the worry is that the server won't cope, then maybe look at using Intersystems API Manager to sit in front of the environment and make use of things like the rate limiting feature.

Or you could go even further and begin caching responses to return if the API is being queried for data that isn't changing much so that there's less processing being done for each request if it's going to get called for the same information by multiple requests in quick succession. You could make your own solution with caché/Iris, or look at something like redis.

Julian Matthews · Feb 2, 2024 go to post

I'm thinking to increase the pool parameter, but I'm not sure if it's a good idea.

If you are not concerned about the order of which you are processing the inbound requests, then upping the pool size to the number of parallel jobs you're looking to run with should do what you need 

However, you may need to then also apply this logic to related components that the Process interacts with, otherwise you will end up just moving the bottleneck to another component.

Alternatively, if it fits your use case, you could use the Actor Pool for your production components and then increase it to a point where you see the bottleneck drop off.

Paolo has provided the link to the documentation on Pools, which has some info on considerations for the use of the two different types of Pool.

Julian Matthews · Dec 22, 2023 go to post

Hi Nimisha.

I see what you mean now.

It does seem like the code within a code block doesn't have access to the methods from Ens.BusinessProcess.

I suspect your only option for is to use the "Call" activity set to synchronous with a "Sync" after it.

Julian Matthews · Dec 22, 2023 go to post

Thanks Luis.

The issue I'd have is that the clock starts on the poll interval at the point the service is started, so a restart of the server/production would then shift the time of day it tries to run, which would not be ideal if I needed a single run at a specific time of day. I might try a combination of the large poll interval and defining a schedule (based on the other responses) and see if that has the desired effect, but I may need to just concede and continue using the task manager. 🙂

Julian Matthews · Dec 22, 2023 go to post

Are you able to share the full error you're seeing?

I have just tested this, and I'm getting no such errors when compiling.

Julian Matthews · Dec 21, 2023 go to post

Hi Mary - thank you for your reply.

Unfortunately the Schedule Option isn't suitable where we need the job to run only once at a set time per day. And, our current solution is very similar to the accepted answer from Ashok Kumar, which is what I was hoping to simplify in some way.

Julian Matthews · Nov 8, 2023 go to post

This may cause some regions to opt for FHIR as a response while others opt for other solutions such as OpenEHR.

There's no reason for these to be seen as competing standards. A model where the data storage is OpenEHR and the data transfer is FHIR is seen by some as the best of both worlds 🙂

Julian Matthews · Oct 11, 2023 go to post

I'm not sure there's a way to select it when creating a production export.

The data in stored within the Global "Ens.Config.BusinessPartnerD" which you could export separately and then import into your new environment?

Julian Matthews · Sep 22, 2023 go to post

Hey Christine.

If I'm reading your question and subsequent replies correctly, you're trying to take the value of PV1:7.1, and then use that in a SQL query. The answer has been given by Ashok when you put their replies together, but hopefully putting it all into a single response will make things easier to follow.

If this is the case, then you will want to do the following:

Step 1: Set a variable to the value of PV1:7.1:

Step 2: Add a code block, and use this to run your sql query:

Step 3: Do what you need to with the value of ID - for the sake of this response, I'm just setting the value of PV1:7.2 to the ID returned from the query that inserted into the variable "Output":


It's worth knowing that, when working with Embedded SQL, prefixing a variable with a colon is how you can pass variables in and out of the Embedded SQL section of code. However it's a bit clearer when working directly with ObjectScript vs a DTL.

For example, if we had the following table:

ID COL_A COL_B
1 ABC 123
2 DEF 234

We could have the following in ObjectScript:

    Set X = "" // X is null
    Set Y = "ABC"
    &SQL(
        SELECT COL_B
        into :X
        From TestTable
        WHERE COL_A = :Y
    )
    WRITE X //X is 123
Julian Matthews · Sep 21, 2023 go to post

I don't believe there is a way of increasing the system limit on string lengths. Even if there is, it's best to approach this by working with the data as a stream.

Otherwise you could end up in a cat and mouse game of needing to increase the length the next time you get a larger document

Julian Matthews · Sep 20, 2023 go to post

The input is a string, so the max length will be your system max (which should be 3,641,144).

Assuming you're trying to retrieve the stream from a HL7 message, you will probably want to use the built in method GetFieldStreamBase64

So you could try something like:

Set tStream = ##class(%Stream.TmpBinary).%New()
Set tSC = pHL7.GetFieldStreamBase64(.tStream,"OBX:5")

And then your decoded file would be in the temp stream.

(You may need to tweak this slightly depending on how you intend to then use the stream, and the correct property path of the Base64 within the HL7 message)

Julian Matthews · Sep 14, 2023 go to post

Stupidly, no.

As the default was set to "TEST" in my function, it worked fine throughout testing. Once the upgrade to Prod occurred, the issue was spotted, and the simple solution was to just reset the values. As I was moving from an adhoc to a official release, I chalked it up to that.

Next time, WRC will be getting a call 🙂