Question Kirsten Whatley · Feb 14

Is there a way to programmatically delete suspended messages?

Is there a way to delete suspended messages rather than using the WebMgmt gui?  

We have over three million in one namespace, and using the gui I can only do 2000 at a time. 

I was thinking of using delete in sql like  Ens.MessageHeader where Status = 'Suspended'

But I don't know if that is a clean way to do it.

Product version: IRIS 2023.1
$ZV: IRIS for UNIX (Red Hat Enterprise Linux 8 for x86-64) 2023.1.4 (Build 580_0_23807U) Mon Nov 25 2024 18:17:37 EST [HealthConnect:5.1.0-3.m4]

Comments

Enrico Parisi · Feb 14

Whatever you use,  portal, code or SQL, I'd suggest NOT to delete the suspended messages. If you delete a message, only the message header will be deleted, leaving all the associated requests/responses orphaned.

I suggest discarding the messages using SQL.

Be careful doing so with a single update SQL statement for 2M messages, with more that 1000 records lock escalation will lock the entire Ens.MessageHeader table/class and your production will have big trouble. To avoid it use %NOLOCK.

Using Display mode:

Update %NOLOCK Ens.MessageHeader set Status ='Discarded' where Status = 'Suspended'

Using Logical or ODBC mode:

Update %NOLOCK Ens.MessageHeader set Status = 4 where Status = 5

0
Kirsten Whatley  Feb 17 to Enrico Parisi

Thank you for the answers! I have a follow up question.

I have been deleting suspended messages already. So how do I go about cleaning up the orphans I've created? 

For instance, in one namespace where I know I deleted some suspended messages (and where all messages older than 7 days are supposed to have been purged), I ran this query:

select * from Ens.MessageHeader nolock
where timecreated < '2025-02-11 00:00:00.000'
order by timecreated 

I get 18298 messages - under four distinct session ids. 

How can I mark these so that will be purged? Or is there some other method of purging these orphans since I deleted their message header? 

Note: These are the settings on our Purge task:

0
Jeffrey Drumm · Feb 15

The class Ens.MessageHeader has a classmethod Purge() that deletes message headers and bodies based on the number of days to keep along with a few other criteria; those are used in an SQL query to select the set of messages to purge. That query along with the associated purge code should work as an example to see what's involved in carefully removing messages without collateral damage ... search table indices need to be maintained, for example.

There's also the Ens.Util.MessagePurge task definition that is used for scheduled purges, it has much of the same code but offers a multi-threaded purge feature that leverages the work queue.

@Enrico Parisi's solution moves the actual purge of the messages off to the scheduled Ens.Util.MessagePurge task for messages that exceed the DaysToKeep limit, as long as that is configured to delete bodies too. This is likely the safer solution and requires significantly less effort 😉

0
Kirsten Whatley · Feb 17

Thank you for the answers! I have a follow up question.

I have been deleting suspended messages already. So how do I go about cleaning up the orphans I've created? 

For instance, in one namespace where I know I deleted some suspended messages (and where all messages older than 7 days are supposed to have been purged), I ran this query:

select * from Ens.MessageHeader nolock
where timecreated < '2025-02-11 00:00:00.000'
order by timecreated 

I get 18298 messages - under four distinct session ids. 

How can I mark these so that will be purged? Or is there some other method of purging these orphans since I deleted their message header? 

Note: These are the settings on our Purge task:

0
Jeffrey Drumm  Feb 17 to Kirsten Whatley

If you're certain that you want to have those messages purged, you can run an UPDATE query against Ens.MessageHeader using the same criteria, but setting the Status field to 4 (Discarded).

Status

ID

Created

1

Queued

2

Delivered

3

Discarded

4

Suspended

5

Deferred

6

Aborted

7

Error

8

Completed

9

0
Enrico Parisi  Feb 17 to Jeffrey Drumm

@Jeffrey Drumm , it's too late, he has already deleted the headers so the bodies are already orphaned.

@Kirsten Whatley , there is no query using the Ens.MessageHeader that can return orphaned message bodies info/reference. The very definition of orphaned messages is that have lost the link form Ens.MessageHeader.

Do you know the MessageBodyClassName of the messages you have deleted?
If it's a custom defined persistent class, can you provide some detail of the message(es) class(es)?

 

0
Jeffrey Drumm  Feb 17 to Enrico Parisi

@Enrico Parisi,

I assumed that she wanted to purge the messages returned by this query:

select * from Ens.MessageHeader nolock
where timecreated < '2025-02-11 00:00:00.000'
order by timecreated

If they'd already been purged, there would be no headers returned.

0
Kirsten Whatley  Feb 21 to Enrico Parisi

I am pretty sure that the bulk of the orphans are this message class:

Class AH.AHLIB.Custom.NextGate.Message.Patient Extends (Ens.Request, %Persistent, %JSON.Adaptor, %XML.Adaptor)

{

Parameter %JSONIGNOREINVALIDFIELD = 1;

Property header As AH.AHLIB.Custom.NextGate.Message.Patient.Header;

Property event As AH.AHLIB.Custom.NextGate.Message.Patient.Event;

Property entity As list Of AH.AHLIB.Custom.NextGate.Message.Patient.Entity;

Property additionalReferences As AH.AHLIB.Custom.NextGate.Message.Patient.AdditonalReferences;

Storage Default

{

<Data name="PatientDefaultData">

<Subscript>"Patient"</Subscript>

<Value name="1">

<Value>header</Value>

</Value>

<Value name="2">

<Value>event</Value>

</Value>

<Value name="3">

<Value>entity</Value>

</Value>

<Value name="4">

<Value>additionalReferences</Value>

</Value>

</Data>

<DefaultData>PatientDefaultData</DefaultData>

<Type>%Storage.Persistent</Type>

}

}

0
Robert Barbiaux  Feb 22 to Kirsten Whatley

You can query the message body class table with a left join to Ens.MessageHeader to get the orphaned message body identifiers :

select 
  %NOLOCK bod.Id
from
  MySample.BodyTable bod
  leftjoin Ens.MessageHeader hdr on bod.Id=hdr.MessageBodyId and hdr.MessageBodyClassName='MySample.BodyTable'where
 hdr.MessageBodyId isNULL
0
Enrico Parisi  Feb 18 to David.Satorres6134

@David.Satorres6134 , in the class EnsPortal.SuspendedMsg,  DeleteMessage() is an instance method and cannot be invoked directly.

Anyway, what it does is simply invoking:

Set tStatus = ##class(Ens.MessageHeader).%DeleteId(pHeaderId)

But, as explained in my previous post, this leave orphaned messages and should be avoided.

0
Kirsten Whatley  Feb 27 to Vic Sun

This is intriguing and might be really helpful, thank you!!

0
Vic Sun  Mar 12 to Kirsten Whatley

Glad you found that interesting! One warning I can add is that if you do decide to add a "no-integrity" type purge and if you've never run one, depending on how many messages you have you may trigger a very large purge (and a lot of journaling activity as a result). Often times when making significant purge changes, it can be safer to increment the time period you are purging bit by bit to make sure you're not asking too much of a single purge.

ex. If you run daily message purges then every day you are purging 1 day's worth. If you have "expired" messages going back a few years and decide to purge them all of a sudden, that might be a surprising number.

0