🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
AsyncTicket
This page was created by Hans.karlsen on 2020-04-14. Last edited by Wikiadmin on 2026-07-29.

AsyncTicket lets you queue work for MDrivenServer to perform later, and is intended for modelers who need background processing from an OCL action.

What AsyncTicket does

AsyncTicket is a model pattern centered on the SysAsyncTicket class. Your OCL creates a ticket, identifies the object to process, and names either a rooted ViewModel or a class method to run. MDrivenServer later resolves the object and performs the requested work in the server context.

Use it when an action should create work now but the work itself can run asynchronously. For example, when a customer is saved, you can queue assignment of its customer number rather than performing that assignment in the initiating action.

This feature is available only with MDrivenServer. Use MDrivenServer and framework versions dated 2020-02-28 or later for the behavior described on this page.

How the ticket lifecycle works

  1. Your OCL creates a SysAsyncTicket and sets RootObject and ViewModel.
  2. The framework resolves RootObject to its external identity and stores that value in RootId. If the root object has not yet been saved, the framework updates and resaves the ticket after the object receives a valid key.
  3. MDrivenServer finds tickets where Done is null. It resolves RootId, finds the named ViewModel or method, and executes it.
  4. When execution finishes, the server sets Done and sets DeleteTime to the completion time plus KeepReceiptMinutes.
  5. A separate low-frequency server job removes tickets whose DeleteTime is older than the current time.

The ticket-processing job runs at high frequency. The cleanup job runs at low frequency. These are administrative serverside jobs created when MDrivenServer recognizes the pattern.

Add the pattern to your model

  1. Open the SysAsync package and merge the SysAsyncTicket model pattern into your model.
  2. Verify that SysAsyncTicket has the attributes and associations described below.
  3. Set the RootObject association to the most abstract class that you will use as an asynchronous root. In a standard model, this is typically SysSuperClass.
  4. Set the RootObject association to Persistent=false.
  5. Create tickets in OCL by assigning the root object and the work identifier in ViewModel.
  6. Deploy the model to MDrivenServer.

SysAsyncTicket model

= Attributes

Member Type Purpose
DeleteTime DateTime? Set by the server after execution to the completion time plus KeepReceiptMinutes. The cleanup job deletes the ticket after this time.
Done DateTime? Set by the server when the requested work has finished. A null value means that the ticket remains eligible for processing.
Error String? Contains information about an initial ticket problem, such as an invalid RootId or a ViewModel that does not exist.
ExecuteEarliest DateTime? Delays execution until the specified time. Leave it null to allow execution as soon as the server can process the ticket.
KeepReceiptMinutes Integer, initial value 10 Defines how many minutes the completed ticket remains available before cleanup. Set an initial value appropriate for your need to inspect completed tickets.
Priority Integer? Optional queue ordering value. When this property exists in the model, tickets are ordered by priority. 1 has higher priority than 10, so a ticket with priority 1 is selected first when both are waiting.
RootId String? The external identity for RootObject, resolved and assigned by the framework.
ViewModel String? Names the rooted ViewModel or class method that MDrivenServer must execute.

= RootObject association

RootObject is a single, transient association to the selected abstract root class. It must be navigable toward that abstract class and must have Persistent=false.

Do not make this association persistent when it targets a highly abstract class. A persistent reference to such a class can require the framework to search every possible subclass for a key. This is called exactification of a foreign key. In a model with hundreds of classes, it can result in hundreds of queries and harm performance.

Choose what the ticket executes

The ViewModel string supports two execution modes.

Mode Value assigned to ViewModel Server behavior
Rooted ViewModel A ViewModel name, preferably through the generated ViewModel constant The server executes the named rooted ViewModel using RootObject as its root. A server-side ViewModel is not required.
Class method ClassName.MethodName The server invokes the method on RootObject after first checking that method's precondition.

= Execute a rooted ViewModel

A rooted ViewModel can be used for work that consists of root-level actions. MDrivenServer executes the root-level actions in top-to-bottom order and saves the changed state produced by those actions.

Use the generated ViewModel constant rather than typing the ViewModel name as a literal string:

let ticket = SysAsyncTicket.Create in (
  ticket.RootObject := self;
  ticket.ViewModel := Customer.ViewModels.SomeViewModel
)

This example executes SomeViewModel with the current Customer as its root object.

= Execute a method

For small units of work, set ViewModel to ClassName.MethodName. The server calls that method for the root object and checks the method precondition before it runs.

For example, a Customer.AssignNumber method can update a shared latest-number value and assign it to the customer:

SomeSingleton.oclSingleton.LatestUsedCustomerNumber :=
  SomeSingleton.oclSingleton.LatestUsedCustomerNumber + 1;
self.CustomerNumber := SomeSingleton.oclSingleton.LatestUsedCustomerNumber

Give the method a precondition when the operation must only run for eligible objects. For this example, use:

self.CustomerNumber->isNull

Create its ticket as follows:

let ticket = SysAsyncTicket.Create in (
  ticket.RootObject := self;
  ticket.ViewModel := 'Customer.AssignNumber'
)

The method form and the ViewModel form are different operations. 'Customer.AssignNumber' invokes a method on the root object; Customer.ViewModels.SomeViewModel invokes a rooted ViewModel.

Delay, retain, and prioritize work

Set optional ticket values before the ticket is processed:

let ticket = SysAsyncTicket.Create in (
  ticket.RootObject := self;
  ticket.ViewModel := 'Customer.AssignNumber';
  ticket.ExecuteEarliest := /* a future DateTime */;
  ticket.KeepReceiptMinutes := 30;
  ticket.Priority := 1
)

In this example, the ticket cannot start before ExecuteEarliest, remains available for 30 minutes after completion, and is placed ahead of tickets with priority values greater than 1 when they are waiting together.

Handle errors and completion

Inspect the ticket receipt when you need to determine what happened:

  • Done set: the server completed the requested work.
  • Done null and Error set: the ticket had an initial problem, for example the root identity could not be resolved or the named ViewModel did not exist.
  • Done null and ExecuteEarliest is in the future: the ticket is waiting for its scheduled earliest execution time.

Keep KeepReceiptMinutes long enough for any process or user that must inspect Done or Error. After DeleteTime, the cleanup job can remove the ticket.

Important: name methods for the actual subclass

When a ticket invokes a method, the class name in ViewModel must match the actual runtime class of self, not only an abstract or base class.

For example, assume the runtime object is SubClassOfThing. Do not queue the method as:

ticket.ViewModel := 'Thing.AssignNumber'

Although the object is formally a Thing, using the base class can cause synchronization problems. MDrivenServer records the changed object as a Thing; when a client refreshes server data, it may receive a notification for a Thing identity that it cannot associate with its SubClassOfThing object.

Name the concrete class instead:

ticket.ViewModel := 'SubClassOfThing.AssignNumber'

Use the following generic form when the root can be a subclass:

ticket.ViewModel := self.oclType.asstring + '.AssignNumber'

See also