Likely significantly faster:


SELECT COUNT(DISTINCT SessionId)
  FROM Ens.MessageHeader
  WHERE ID >= (SELECT TOP 1 ID FROM Ens.MessageHeader WHERE TimeCreated >='2025-02-01 00:00:00.000' ORDER BY TimeCreated ASC)
    AND ID <= (SELECT TOP 1 ID FROM Ens.MessageHeader WHERE TimeCreated <='2025-02-28 23:59:59.999' ORDER BY TimeCreated DESC)

In my crude and hasty benchmarking, twice as fast on a sampling of  2.7M message headers.

You can suspend the task via either ObjectScript or SQL.

Assuming you know the Task ID, and using 1001 as an example:

Set tsk = ##class(%SYS.Task).%OpenId(1001)
Set tsk.Suspended = 1
Do tsk.%Save()

or

Do ##class(%SYS.Task).Suspend(1001,1)

Resume the task with

Do ##class(%SYS.Task).Resume(1001)

Via SQL:

UPDATE %SYS.Task SET Suspended = 1 WHERE ID = 1001

or

UPDATE %SYS.Task SET Suspended = 1 WHERE TaskClass = 'Sample.Util.CreateTask'

Set Suspended to 0 to re-enable the task.

Something like this should do the trick:

ClassMethod GetConnectionStatus(pItemName As %String, ByRef pStatus As %String, ByRef pState As %String) As %Status [ Language = objectscript ]
{
	Set tStatement = ##class(%SQL.Statement).%New()
	Set tSC = tStatement.%PrepareClassQuery("Ens.Util.Statistics","EnumerateJobStatus")
	Return:$$$ISERR(tSC) tSC
	Set tRS = tStatement.%Execute(pItemName)
	If tRS.%SQLCODE = 0
	{
		Do tRS.%Next()
		Set pStatus = tRS.%Get("Status")
		Set pState  = tRS.%Get("AdapterState")
		Return $$$OK
	}
	Return $$$ERROR(5001,"Status not Found")
}

Call it with the status and state variables passed by reference:

Set sc=##class(My.Class).GetConnectionStatus("T_SPM_SIU",.status,.state)

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 😉

Looping back to this.

I thought it might be more user-friendly to define custom property parameters for this solution to specify both length (LENGTH) and alignment (ALIGN), rather than just relying on MAXLEN. I'd also of course modify the code to be generated to handle the desired formatting.

I've defined a a property parameter class with those parameters and added it as a PropertyClass to the class containing the property definition, but on compilation I get:

ERROR #5002: ObjectScript error: <UNDEFINED>Get+1^rcc.FixStr.G1 *%parameter("LENGTH")
  > ERROR #5490: Error running generator for method 'Get:rcc.FixStr'  

What am I missing? Here's the property class:

Class rcc.FixStr.Props
{

Parameter LENGTH As %String = "10";
Parameter ALIGN As %String = "LEFT";
}

And the class containing the Get() method for the property definition:

Class rcc.FixStr Extends %RegisteredObject [ PropertyClass = rcc.FixStr.Props ]
{

/// Fill value <var>%val</var> to <a href="#MAXLEN">MAXLEN</a> characters.
Method Get() As %String [ CodeMode = generator, ServerOnly = 1 ]
{
	set tCode="$e(%val_"""_$j("",+%parameter("LENGTH"))
	set tCode=tCode_""",1,"_+%parameter("LENGTH")_")"
	$$$GENERATE( "  Quit "_tCode)
	QUIT $$$OK
}

}