Understanding the Draft Concept

Objectives

After completing this lesson, you will be able to:
  • Explain the need for draft in stateless applications.
  • Enable draft handling in the Business Object.

Draft Motivation

Stateful transactional applications with a server session

Traditional ABAP applications are developed using stateful technologies, such as Floorplan Manager for Web Dynpro ABAP or the classical screen (Dynpro) technique.

These stateful transactional applications rely on a server session along with application buffers that can fulfill client requests (user interactions with multiple backend round trips) until the user has saved the data changes and finished their work. In stateful applications, data entry and data updates work on a temporary in-memory version of a business entity which is only persisted once it is sufficiently complete and consistent.

Stateless transactional applications with many individual session

Modern cloud-ready apps require a stateless communication pattern,for example, to use cloud capabilities like elasticity and scalability. Therefore, statelessness is one of the key principles of RESTful protocols like OData. In stateless scenarios, there is no fixed backend session along a business transaction and the incoming requests can be dispatched to different backend resources, which support load balancing. As a consequence, the application cannot save a temporary version of the business entity inside the application.

Diagram illustrating Stateless Technologies - Non-draft Scenario

Without special precautions, an application that is based on a RAP service does not save temporary versions on the application server. When the user chooses Save, the data is sent to the backend, checked, completed, and persisted on the database in one single session. It is referred to as the non-draft scenario.

Draft persistence on database

Both, the stateful approach and the stateless non-draft approach, share one disadvantage: the end user cannot store inconsistent interim versions of the data to continue at a later point in time or to recover this data, in case the application terminates unexpectedly.

While the stateful scenario holds interim versions of the data in the session on the application server, the draft scenario stores interim versions of the data on the database. These persistent temporary versions are known as Drafts. Drafts are not stored in the same database tables as the active versions but in special database tables, the Draft Tables. The draft represents the state of the data and stores the transactional changes until they are persisted in the active table or discarded.

An overview of draft

Draft Enabled RAP Business Objects

Enable draft in behavior definition

The draft capability for a draft business object is defined by adding the statement with draft; in the header part of the behavior definition. You can build draft business objects from scratch, or you can draft-enable existing business objects with both implementation types, managed, or unmanaged. 

Draft enabled business objects

In all scenarios, the draft is managed. It means that the draft life cycle is determined by the RAP draft-runtime when the business object is draft-enabled. You, as an application developer, do not need to know how the draft instance is created, how draft data is written to the draft database table, or how the draft instance is activated.

Adding draft capabilities to your business object implies changes in your business logic for the processing of active data that you are responsible for. In addition, RAP also offers implementation exits for cases in which you need business service-specific draft capabilities that impact the draft handling.

Life cycle of draft 1 - creating a new instance

The fact that two database tables are involved in the runtime of a draft business object requires an elaborate life cycle of data. All data undergo several states and state transitions (actions) while being processed by a draft business object. There are two scenarios that must be distinguished: New-Draft and Edit-Draft.

We speak of New-Draft instances if the data is initial data that is not yet persisted on the active database table. New-draft instances do not have a corresponding active instance.

The life cycle of a new-draft starts with the creation of a new draft instance. The new data is immediately stored in the draft persistence, regardless of its validity or completeness. Draft data can be enriched and checked for consistency by execution of the PREPARE action.

With the ACTIVATE action, the draft data is copied into active data in the application buffer. ACTIVATE includes an implicit execution of PREPARE. Once the active instance is successfully created, the draft instance is discarded and the related data is deleted from the draft table.

Note

The ACTIVATE action does not save the active instance on the database. The actual save is executed separately, either by COMMIT ENTITIES via EML or by calling the save sequence in case of OData.

Using the DISCARD action on a New-Draft deletes the related data from the application buffer and the draft table without creating an active instance.

Life cycle of a draft 2 - editing existing instance

Edit-Drafts always exist in parallel to the corresponding active data. They are created by using the EDIT action on active instances. The whole active instance is copied to the draft table.

Like New-Drafts, Edit-Drafts can be enriched and validated using the PREPARE action.

With the ACTIVATE action, the draft data is copied to overwrite the existing active data in the application buffer. As with New-Drafts, ACTIVATE includes an implicit execution of PREPARE.

Using the DISCARD action on an Edit-Draft deletes the draft data from the application buffer and the draft table, leaving the active data in the application buffer unchanged.

Draft Tables

Quick fix to generate draft database table

Draft business objects need two separate database tables for each entity, one for the active persistence and one for storing draft instances. With using a separate database table for the draft information, it is guaranteed that the active persistence database table remains untouched and consistent for existing database functionality.

While the persistent table addition is used to specify the active table of a RAP BO entity, the draft table is assigned via the draft table addition. When the business object is draft-enabled via the with draft statement, the draft table addition is mandatory in every behavior definition statement.

If the draft table does not yet exist, you can use a quick fix to generate the draft table.

Note

A similar quick fix is available later, to adjust the draft table after you changed the CDS view entity.

Draft database table layout

The draft database table is based on the CDS view entity definition. The draft table contains exactly one field for each view element plus technical information which the RAP runtime needs to handle the drafts. The technical information is added using the draft admin include SYCH_BDL_DRAFT_ADMIN_INC.

Note

The RAP runtime ignores view elements which are listed after a field( suppress ). So, no corresponding field is added to in the draft table for those view elements.

Concurrency Control in Draft

Pessimistic concurrency control in draft

When handling drafts, the RAP runtime uses a combination of pessimistic and optimistic concurrency control to ensure data consistency.

When a draft instance is created for an existing active instance, the active instance receives an exclusive lock. This means that it is not possible to create a second draft for this active instance or change the active instance directly. The exclusive lock is not bound to the ABAP session. It remains in place between the different update requests from the same user. When the user saves or discards the changes, the draft is deleted and the exclusive lock is removed.

Optimistic concurrency control in draft

Even though the exclusive lock is not bound to a session, it is discarded after a certain period of time. By default, the maximum lifetime for an exclusive lock is set to 15 minutes. Once the exclusive lock expires after this duration time, the pessimistic lock phase ends and optimistic lock phase begins.

During the optimistic lock phase, the draft still exists, but it is not protected by an exclusive lock. This means that during the optimistic lock phase, another user can change the active instance directly or create a second draft for the same active instance.

End of optimistic lock phase 1 - Draft Timeout

If a draft remains untouched for a certain period of time, the draft life cycle service removes it automatically. By default, it happens after 28 days. It implicitly ends the optimistic lock phase of the draft.

End of optimistic lock phase 2 - Discard Draft

The draft owner that is the user that created the draft, can end the optimistic lock phase by discarding the draft explicitly. It can be the case if the data changes are no longer relevant.

End of optimistic lock phase 3 - Draft Resume

If the draft is still alive and valid, the draft owner is able to continue to work on the draft instance. If the draft owner resumes editing the draft, the optimistic lock phase ends and a new pessimistic lock phase begins, which means that a new exclusive lock is set for the corresponding active document.

A way to avoid resume of outdated draft

During the optimistic lock phase, the active instance is not protected by an exclusive lock. Therefore, it is possible for another user to change the active instance, either directly or through a second draft. If the other user creates a second draft, the first draft is deleted implicitly and it is not possible to resume it. But if the other user changed the active instance directly, the draft still exists. However, this draft is no longer valid because it does not reflect the latest changes on the active data.

To avoid data inconsistencies, the framework has to ensure that it is not possible to resume editing a draft once it has become invalid.

ETag Fields in Draft

Total ETag in draft scenario

The RAP runtime framework uses an ETag field mechanism to identify outdated drafts. If the value of the ETag field is still the same in the active instance and in the draft instance, the draft is still valid and resuming is possible. If the value of the ETag field value differs in the active instance and in the draft instance, the active instance was changed after the draft was created. The draft is invalid and resuming editing is not allowed.

The ETag field to identify invalid draft instances is defined in the define behavior statement, by adding total etag after the lock master addition.

Note

In draft-enabled RAP BOs, it is mandatory to define a total ETag field.

The field to be used as a Total ETag field has to meet the following requirements:

  • The Total ETag field value in the active version always changes when the active version is changed
  • The Total ETag field value in a draft instance does not change during the draft lifetime

The administrative field annotated with @Semantics.systemdateTime.lastchangedAt: true, which we used as ETag master field earlier, meets these requirements.

ETag Master in draft scenario

On the other hand, the field annotated with @Semantics.systemdateTime.lastchangedAt: true is not suitable as ETag master anymore if the business object is draft-enabled. To ensure the optimistic concurrency control of the OData protocol works properly, the ETag master field has to receive a new value whenever there is an update of the draft instance. However, the lastChangedAt time stamp only changes when the draft is persisted.

To support OData concurrency control, SAP introduced the @Semantics.systemDateTime.localInstanceLastChangedAt: true annotation. The RAP runtime framework updates the field that is annotated like this during every write access to the draft instance.

Note

You can consider the field that is annotated with @Semantics.systemDateTime.localInstanceLastChangedAt: true as a time stamp for the last change to the draft instance ("local Instance"). While the field annotated with @Semantics.systemdateTime.lastchangedAt: true contains the time stamp for the last change to the active instance.

Draft Actions

Various draft actions that are implicitly available for draft-enabled business objects

Draft actions are actions that are implicitly available for draft-enabled business objects. They only exist for lock master entities, as they always refer to the whole lockable subtree of a business object.

All draft actions are implicitly available in EML and exposed to OData, even without explicitly declaring them in the behavior definition. In strict mode, however, the syntax check issues error messages until you explicitly declare all draft actions.

The following draft actions exist:

Draft Action EDIT

The draft action EDIT copies an active instance to a draft instance and stores the edit-draft in the draft database table. Feature and authorization control is available for the EDIT, which you can optionally define to restrict the usage of the action.

Draft Action ACTIVATE

The draft action ACTIVATE is the inverse action to EDIT. It invokes the PREPARE action and an UPDATE operation with all the changes for the active instance in case of an edit-draft, or a CREATE operation in case of a new-draft. Once the active instance is successfully stored on the database, the draft instance is discarded and the related data is removed from the draft table.

In contrast to the EDIT action, the ACTIVATE action does not allow feature or authorization control. Authorization is controlled for the modifying operation with which the active instance is saved to the database.

Draft Action DISCARD

The draft action DISCARD deletes the draft instance and removes the related data from the draft database table. No feature or authorization control can be implemented.

Draft Action RESUME

The draft action RESUME is executed when a user continues to work on a draft instance after the exclusive lock for the active data has already expired. It re-creates the lock for the corresponding instance on the active database table. On an SAP Fiori elements UI, it is invoked when reopening and changing a draft instance after the exclusive lock expired.

In case of a new draft, the same feature and authorization control is executed as defined for a CREATE operation. In case of an edit-draft, the same feature and authorization control is executed as defined for the draft action EDIT.

As the RESUME action is application-specific, it is only exposed to OData if it is explicitly declared in the behavior definition. You can only execute the RESUME action via EML if the action is explicitly made available in the behavior definition.

Draft Action PREPARE (Determine Action)

The draft action PREPARE is a determine action. This means that you can assign validations and determinations to this action which are then executed when the action is triggered. Assigning validations and determinations to the draft determine action PREPARE allows you to validate and adjust draft data before the transition to active data. It is also possible to not define any determinations or validations to the draft determine action PREPARE.

Note

We discuss and determine actions in general later in this course.
Projection of draft-handling

When you define a BO projection or a BO interface on top of a draft-enabled business object, it is up to you to decide whether the projection or interface is also draft-enabled or not. If you want it to be draft-enabled, you have to add the use draft statement after the initial projection or interface statement.

The draft table(s) and total ETag fields are implementation details that are automatically reused and do not have to be specified again.

The draft actions are also reused implicitly. However, in strict syntax mode, it is enforced that all of them are listed with a use action statement.

Note

Remember, that in behavior interface definitions, the syntax check applies the strict(2) syntax rules by default.

Enable Draft Handling in the Business Object

In this exercise, you enable draft handling in your business object and in your OData UI service. To properly support optimistic concurrency control in the OData service, you extend the data model with a timestamp field that is updated during every save of a draft.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
Database Table/LRN/437D_TRAVEL
CDS Data Definition (Model)/LRN/437D_R_TRAVEL
CDS Data Definition (Projection)/LRN/437D_C_TRAVEL
CDS Metadata Extension/LRN/437D_C_TRAVEL
CDS Behavior Definition (Model)/LRN/437D_R_TRAVEL

Task 1: Define a Timestamp Field for Draft Changes

Extend your database table Z##_TRAVEL with a timestamp field for changes to the draft instance (suggested name: loc_changed_at).

Add the field to your data model view entity Z##_R_Travel and annotate it with the necessary sub annotation of the @Semantics annotation to ensure that the runtime updates the field during every save of a draft.

Add the field to the field mapping in the behavior definition on data model level Z##_R_TRAVEL, disallow direct changes and use it as the etag field for optimistic concurrency control.

Add the field to your projection view entity Z##_C_Travel and extend the metadata extension, to keep the field hidden in the OData UI service.

Steps

  1. Edit the definition of your database table Z##_TRAVEL. At the end of the field list, add a loc_changed_at field based on data element ABP_LOCINST_LASTCHANGE_TSTMPL.

    1. Open the definition of your database table.

    2. At the end of the field list, add the following code:

      Code Snippet
      1
      loc_changed_at : abp_locinst_lastchange_tstmpl;
  2. Activate the database table.

    1. Press Ctrl + F3 to activate the development object.

  3. Edit the definition of your data model view entity Z##_R_Travel. Add the new table field to the element list and specify the alias name LocChangedAt.

    1. Adjust the code as follows:

      Code Snippet
      12345
      @Semantics.systemDateTime.lastChangedAt: true changed_at as ChangedAt, @Semantics.user.lastChangedBy: true changed_by as ChangedBy, loc_changed_at as LocChangedAt
  4. Add annotation @Semantics.systemDateTime.localInstanceLastChangedAt: true to the new view element and activate the data definition.

    1. Adjust the code as follows:

      Code Snippet
      123456
      @Semantics.systemDateTime.lastChangedAt: true changed_at as ChangedAt, @Semantics.user.lastChangedBy: true changed_by as ChangedBy, @Semantics.systemDateTime.localInstanceLastChangedAt: true loc_changed_at as LocChangedAt
    2. Press Ctrl + F3 to activate the development object.

  5. Edit the behavior definition on data model level Z##_R_TRAVEL. After the etag master addition of the define behavior statement, replace the timestamp for changes to the active data ChangedAt with the new timestamp field LocChangedAt.

    1. Adjust the code as follows:

      Code Snippet
      1
      etag master LocChangedAt //ChangedAt
  6. Locate the FIELD statement for the STATUS field and the administrative fields, and add the new field.

    1. Adjust the code as follows:

      Code Snippet
      12345
      field ( readonly ) Status, ChangedAt, ChangedBy, LocChangedAt;
  7. Scroll down to the mapping for statement and add the mapping for the new field. Then activate the behavior definition.

    1. Adjust the code as follows:

      Code Snippet
      123
      ChangedAt = changed_at; ChangedBy = changed_by; LocChangedAt = loc_changed_at;
    2. Press Ctrl + F3 to activate the development object.

  8. Edit the definition of your projection view entity Z##_C_Travel. At the end of the element list, add the new element and activate the data definition.

    1. Adjust the code as follows:

      Code Snippet
      123
      ChangedAt, ChangedBy, LocChangedAt
    2. Press Ctrl + F3 to activate the development object.

  9. Edit the metadata extension for the projection view Z##_C_TRAVEL. Add the new element LocChangedAt with annotation @UI.hidden: true, and activate the metadata extension.

    1. Adjust the code as follows:

      Code Snippet
      12345678
      @UI.hidden: true ChangedAt; @UI.hidden: true ChangedBy; @UI.hidden: true LocChangedAt;
    2. Press Ctrl + F3 to activate the development object.

Task 2: Enable Draft-Handling in the Business Object

In the behavior definition on data model level Z##_R_TRAVEL, add the necessary statement to enable draft handling. Define and generate a draft table for the travel data (suggested name: Z##_TRAVEL_D). Use the timestamp for changes to the active data ChangedAt as the total etag field and add the draft actions to the behavior definition.

Steps

  1. Edit the behavior definition on data model level Z##_R_TRAVEL and add the with draft; statement.

    1. Adjust the code as follows:

      Code Snippet
      123456
      managed implementation in class zbp_##_r_travel unique; strict ( 2 ); with draft; define behavior for Z##_R_Travel alias Travel
  2. Scroll down to the DEFINE BEHAVIOR statement. After persistent table z##_travel add draft table, followed by the name of the draft table that you want to create.

    1. Adjust the code as follows:

      Code Snippet
      1234
      define behavior for Z##_R_Travel alias Travel persistent table z##_travel draft table z##_travel_d lock master
  3. Use a quick fix to generate the draft table.

    1. Place the cursor on the name of the draft table.

    2. Press Ctrl + 1 to invoke the quick assist proposals.

    3. From the list of available quick fixes, choose Create draft table z##_travel_d for entity z##_r_travel.

    4. If desired, adapt the generated description for the draft table and choose Next.

    5. Confirm the transport request and choose Finish.

  4. Analyze the generated database table, then activate it.

    1. The definition of the database table is already opened after the previous step.

    2. Press Ctrl + F3 to activate the development object.

  5. Return to the behavior definition. After lock master, add total etag followed by the name of the timestamp field for changes to the active data (ChangedAt).

    1. Adjust the code as follows:

      Code Snippet
      123456789
      define behavior for Z##_R_Travel alias Travel persistent table z##_travel draft table z##_travel_d lock master total etag ChangedAt authorization master ( instance ) etag master LocChangedAt // ChangedAt early numbering {
  6. Explicitly declare the draft actions Edit, Activate, Discard, Resume, and the draft determine action Prepare.

    For now, ignore the warnings regarding the missing addition optimized for the Activate action and the missing assignment of the validations to the draft action Prepare.
    1. Add the following code:

      Code Snippet
      12345
      draft action Edit; draft action Activate; draft action Discard; draft action Resume; draft determine action Prepare;
  7. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.