This chapter is for Bootcamp learners who want to generate car registration numbers safely when multiple users create cars at the same time.
This chapter is for Bootcamp learners who want to generate car registration numbers safely when multiple users create cars at the same time.
Template loop detected: Training:Bootcamp:Chapter 15
Before you begin
Complete Training:Bootcamp:Chapter 14 and make sure that your model can upload to a running MDrivenServer. This chapter changes the model and relies on MDrivenServer evolving the database schema when you upload it.
The registration-number format used in this chapter is ABC123. The first part consists of three letters and the second part is a number. The exercise first implements generation locally, then shows why local generation is not safe across concurrent server contexts, and finally uses the SysAsyncTicket pattern to serialize the assignment.
What you will build
You will:
- create a
Car.GetNewRegistrationNumber():Stringmethod; - store the current sequence state on
SysSingleton; - use the debugger and the OCL stepper to diagnose an expression;
- assign a registration number when a
Carenters theBrandNewstate; - import the
SysAsyncTicketmodel pattern and schedule the assignment throughDoAsync; - make
RegistrationNumberread-only in the relevant ViewModels; and - mark the attribute as
Realtime=trueso the assigned value returns to the client sooner.
Part 1: Generate the next registration number
Create the method and sequence state
- On class
Car, create the methodGetNewRegistrationNumber():String. - On
SysSingleton, add the following nullable integer attributes. They retain the state needed to calculate the next value.
| Attribute | Initial value | Purpose |
|---|---|---|
RegNumberLetter1Current:Integer?
|
65
|
Current value for the first letter |
RegNumberLetter2Current:Integer?
|
65
|
Current value for the second letter |
RegNumberLetter3Current:Integer?
|
65
|
Current value for the third letter |
RegNumberNumberPartCurrent:Integer?
|
1
|
Current numeric part |
- For each of the three letter attributes, set both the default value and
InitialValueto65. - Set
InitialValueforRegNumberNumberPartCurrentto1. - Implement the method logic shown in Video 15. It must return a string in the
ABC123form, advance the stored sequence state, and carry to the next letter when a letter reachesZ.
The value 65 is the starting value used by this exercise for the letter sequence. Test the rollover behavior later by repeatedly executing the method until a letter reaches Z.
Use semicolons in an OCL action expression
An OCL expression returns one result. When an action expression must perform several operations, separate the expressions with a semicolon (;). The result of the complete expression is the result of its last expression.
| Expression | Result |
|---|---|
expr1;expr2
|
The result of expr2
|
expr1;expr2;expr3
|
The result of expr3
|
expr1;expr2;
|
Invalid: an expression cannot end with a semicolon |
For example, use earlier expressions to update the sequence state and make the final expression produce the registration-number string. Do not leave a trailing semicolon after that final result.
Upload and test the method in the debugger
- Save the model and upload it to MDrivenServer. Confirm that the server status reports the database evolution for the new
SysSingletonattributes. - Select Play, open the debugger, and select MDrivenServer persistence.
- Open a debugger search window for
Car, search for cars, and drag a car to the green expression dot. This assigns that car toselffor the debugger expression. - Change the expression mode to Action and execute
self.GetNewRegistrationNumber. - If the result or stored values are not what you expect, start the expression with
Step, select Start, and use Step into to enter the method. Follow each expression and identify where the returned result or sequence update differs from the intended behavior. - Correct the method, save, and upload the model again.
- If the debugger is still open, select Re-Read Model. Uploading alone does not update the model already loaded by the debugger.
- Repeat
self.GetNewRegistrationNumberwithF5. HoldF5to execute repeatedly and observe the rollover fromZto the next letter position. - Cancel the test changes when you finish.
Part 2: Assign a number when a car is created
Add the state entry action
- Open the state diagram for
Car. - On state
BrandNew, add an entry action:
self.RegistrationNumber:=self.GetNewRegistrationNumber- Save and upload the model to MDrivenServer.
- In the browser application, create a new
Car. Verify that the new car receives a registration number.
At this stage, the assignment can work for one user but is not safe for concurrent users.
Reproduce the concurrency issue
- Open the application in two independent browser contexts. For example, use a normal window and an incognito window, and sign in separately.
- Use an account that has permission to create a new car. The
b@b.seuser does not have this permission in the Bootcamp model; usea@a.sewhere needed. - Place both windows on the Car Seeker page.
- Select New Car in both windows as close together as possible.
- Observe that both new cars can receive the same registration number.
This conflict occurs because both server contexts can read the same current sequence state before either update is committed. A system should keep unrelated work asynchronous so users are not blocked by lengthy operations. For a shared number sequence, however, generation and assignment must be serialized: only one matching assignment job may run at a time.
Part 3: Serialize assignment with SysAsyncTicket
Import the pattern
- Download the
SysAsyncTicketmerge model from the MDriven wiki and merge it into your model. - Verify that your active packages use
SysSuperClassas their default superclass. Classes without an explicit superclass then inherit fromSysSuperClass. - Confirm that the imported pattern makes the
DoAsyncmethod available through that inheritance.
SysAsyncTicket is the pattern recognized by MDrivenServer for asynchronous work. Calling DoAsync moves the operation to the server. MDrivenServer serializes jobs with the same signature, which prevents two cars from receiving the same registration number through this assignment operation.
Move the assignment into an asynchronous method
- Create a method on
CarnamedActuallyAssignNewNumber. - Move the former entry-action assignment into that method:
self.RegistrationNumber:=self.GetNewRegistrationNumber- Replace the
BrandNewentry action with:
self.DoAsync('ActuallyAssignNewNumber')- Save and upload the model.
- Create a new car in the application and save it. Wait for the registration number to return from MDrivenServer.
- Repeat the two-browser test. Create new cars in both contexts repeatedly. Verify that no two new cars receive the same number.
Inspect asynchronous work on MDrivenServer
- Open the MDrivenServer administration UI at
http://localhost:5000, or use the port configured in the Cloud Connect dialog. - Sign in with the Bootcamp administrator account: user
a, password123456. - Open Running then PeriodicActions. Review the actions that inspect
SysAsyncTicketrecords and remove old tickets. - Open Running then Data in A0. Select
SysAsyncTicket, then select Search. Locate ticket data forCar.ActuallyAssignNewNumberand check that no issue is reported. - Open Running then WorkInfo to inspect the server events recorded for the work.
Prevent manual edits to registration numbers
The registration number is system-assigned. Make it read-only wherever the UI exposes it, so users do not mistake it for editable data.
- Select the
RegistrationNumberattribute onCarand use Changed by - Cross Reference. - Open
ViewModelColumn ProperCarView.RegistrationNumber R/W. - Select
RegistrationNumberand set its Readonly Expression totrue. - Run Changed by - Cross Reference again. It also identifies
TheTemplateForCarTransferOwnershipDocumentReport. - Open that ViewModel and set the
RegistrationNumbercolumn's Readonly Expression totrue.
You can use IsStatic instead when it fits the intended UI, but this exercise uses a read-only expression to make the restriction explicit in each ViewModel.
Mark the result as realtime
- Select
Car.RegistrationNumber. - Open the Tagged Values dialog.
- Select Refresh From Wiki.
- Select <Create Empty TV>, choose
Realtime, and set its value totrue. - Close the dialog, save the model, and upload it to MDrivenServer.
- Create another new car and verify that its registration number returns faster than before.
Check your result
Before continuing, verify all of the following:
- A new car receives a registration number in the expected format.
- Repeated method calls advance the sequence and handle letter rollover.
- Two independent browser contexts cannot obtain the same number for new cars.
- MDrivenServer records the asynchronous assignment as
Car.ActuallyAssignNewNumberwork. - Registration numbers are read-only in both identified ViewModels.
Car.RegistrationNumberhas theRealtime=truetagged value.
The chapter download is available as AfterChapter15.zip.
See also
- Training:Bootcamp:Chapter 14
- Training:Bootcamp:Chapter 12
- Training:Bootcamp:Chapter 16
- Training:Bootcamp:Chapter 1
Before you begin
Complete Training:Bootcamp:Chapter 14 and make sure that your model can upload to a running MDrivenServer. This chapter changes the model and relies on MDrivenServer evolving the database schema when you upload it.
The registration-number format used in this chapter is ABC123. The first part consists of three letters and the second part is a number. The exercise first implements generation locally, then shows why local generation is not safe across concurrent server contexts, and finally uses the SysAsyncTicket pattern to serialize the assignment.
What you will build
You will:
- create a
Car.GetNewRegistrationNumber():Stringmethod; - store the current sequence state on
SysSingleton; - use the debugger and the OCL stepper to diagnose an expression;
- assign a registration number when a
Carenters theBrandNewstate; - import the
SysAsyncTicketmodel pattern and schedule the assignment throughDoAsync; - make
RegistrationNumberread-only in the relevant ViewModels; and - mark the attribute as
Realtime=trueso the assigned value returns to the client sooner.
Part 1: Generate the next registration number
Create the method and sequence state
- On class
Car, create the methodGetNewRegistrationNumber():String. - On
SysSingleton, add the following nullable integer attributes. They retain the state needed to calculate the next value.
| Attribute | Initial value | Purpose |
|---|---|---|
RegNumberLetter1Current:Integer?
|
65
|
Current value for the first letter |
RegNumberLetter2Current:Integer?
|
65
|
Current value for the second letter |
RegNumberLetter3Current:Integer?
|
65
|
Current value for the third letter |
RegNumberNumberPartCurrent:Integer?
|
1
|
Current numeric part |
- For each of the three letter attributes, set both the default value and
InitialValueto65. - Set
InitialValueforRegNumberNumberPartCurrentto1. - Implement the method logic shown in Video 15. It must return a string in the
ABC123form, advance the stored sequence state, and carry to the next letter when a letter reachesZ.
The value 65 is the starting value used by this exercise for the letter sequence. Test the rollover behavior later by repeatedly executing the method until a letter reaches Z.
Use semicolons in an OCL action expression
An OCL expression returns one result. When an action expression must perform several operations, separate the expressions with a semicolon (;). The result of the complete expression is the result of its last expression.
| Expression | Result |
|---|---|
expr1;expr2
|
The result of expr2
|
expr1;expr2;expr3
|
The result of expr3
|
expr1;expr2;
|
Invalid: an expression cannot end with a semicolon |
For example, use earlier expressions to update the sequence state and make the final expression produce the registration-number string. Do not leave a trailing semicolon after that final result.
Upload and test the method in the debugger
- Save the model and upload it to MDrivenServer. Confirm that the server status reports the database evolution for the new
SysSingletonattributes. - Select Play, open the debugger, and select MDrivenServer persistence.
- Open a debugger search window for
Car, search for cars, and drag a car to the green expression dot. This assigns that car toselffor the debugger expression. - Change the expression mode to Action and execute
self.GetNewRegistrationNumber. - If the result or stored values are not what you expect, start the expression with
Step, select Start, and use Step into to enter the method. Follow each expression and identify where the returned result or sequence update differs from the intended behavior. - Correct the method, save, and upload the model again.
- If the debugger is still open, select Re-Read Model. Uploading alone does not update the model already loaded by the debugger.
- Repeat
self.GetNewRegistrationNumberwithF5. HoldF5to execute repeatedly and observe the rollover fromZto the next letter position. - Cancel the test changes when you finish.
Part 2: Assign a number when a car is created
Add the state entry action
- Open the state diagram for
Car. - On state
BrandNew, add an entry action:
self.RegistrationNumber:=self.GetNewRegistrationNumber- Save and upload the model to MDrivenServer.
- In the browser application, create a new
Car. Verify that the new car receives a registration number.
At this stage, the assignment can work for one user but is not safe for concurrent users.
Reproduce the concurrency issue
- Open the application in two independent browser contexts. For example, use a normal window and an incognito window, and sign in separately.
- Use an account that has permission to create a new car. The
b@b.seuser does not have this permission in the Bootcamp model; usea@a.sewhere needed. - Place both windows on the Car Seeker page.
- Select New Car in both windows as close together as possible.
- Observe that both new cars can receive the same registration number.
This conflict occurs because both server contexts can read the same current sequence state before either update is committed. A system should keep unrelated work asynchronous so users are not blocked by lengthy operations. For a shared number sequence, however, generation and assignment must be serialized: only one matching assignment job may run at a time.
Part 3: Serialize assignment with SysAsyncTicket
Import the pattern
- Download the
SysAsyncTicketmerge model from the MDriven wiki and merge it into your model. - Verify that your active packages use
SysSuperClassas their default superclass. Classes without an explicit superclass then inherit fromSysSuperClass. - Confirm that the imported pattern makes the
DoAsyncmethod available through that inheritance.
SysAsyncTicket is the pattern recognized by MDrivenServer for asynchronous work. Calling DoAsync moves the operation to the server. MDrivenServer serializes jobs with the same signature, which prevents two cars from receiving the same registration number through this assignment operation.
Move the assignment into an asynchronous method
- Create a method on
CarnamedActuallyAssignNewNumber. - Move the former entry-action assignment into that method:
self.RegistrationNumber:=self.GetNewRegistrationNumber- Replace the
BrandNewentry action with:
self.DoAsync('ActuallyAssignNewNumber')- Save and upload the model.
- Create a new car in the application and save it. Wait for the registration number to return from MDrivenServer.
- Repeat the two-browser test. Create new cars in both contexts repeatedly. Verify that no two new cars receive the same number.
Inspect asynchronous work on MDrivenServer
- Open the MDrivenServer administration UI at
http://localhost:5000, or use the port configured in the Cloud Connect dialog. - Sign in with the Bootcamp administrator account: user
a, password123456. - Open Running then PeriodicActions. Review the actions that inspect
SysAsyncTicketrecords and remove old tickets. - Open Running then Data in A0. Select
SysAsyncTicket, then select Search. Locate ticket data forCar.ActuallyAssignNewNumberand check that no issue is reported. - Open Running then WorkInfo to inspect the server events recorded for the work.
Prevent manual edits to registration numbers
The registration number is system-assigned. Make it read-only wherever the UI exposes it, so users do not mistake it for editable data.
- Select the
RegistrationNumberattribute onCarand use Changed by - Cross Reference. - Open
ViewModelColumn ProperCarView.RegistrationNumber R/W. - Select
RegistrationNumberand set its Readonly Expression totrue. - Run Changed by - Cross Reference again. It also identifies
TheTemplateForCarTransferOwnershipDocumentReport. - Open that ViewModel and set the
RegistrationNumbercolumn's Readonly Expression totrue.
You can use IsStatic instead when it fits the intended UI, but this exercise uses a read-only expression to make the restriction explicit in each ViewModel.
Mark the result as realtime
- Select
Car.RegistrationNumber. - Open the Tagged Values dialog.
- Select Refresh From Wiki.
- Select <Create Empty TV>, choose
Realtime, and set its value totrue. - Close the dialog, save the model, and upload it to MDrivenServer.
- Create another new car and verify that its registration number returns faster than before.
Check your result
Before continuing, verify all of the following:
- A new car receives a registration number in the expected format.
- Repeated method calls advance the sequence and handle letter rollover.
- Two independent browser contexts cannot obtain the same number for new cars.
- MDrivenServer records the asynchronous assignment as
Car.ActuallyAssignNewNumberwork. - Registration numbers are read-only in both identified ViewModels.
Car.RegistrationNumberhas theRealtime=truetagged value.
The chapter download is available as AfterChapter15.zip.
