Skip to content

so5extra 1.5 Revocable Messages

Yauheni Akhotnikau edited this page Dec 29, 2021 · 1 revision

Introduction

A new feature has been introduced in SObjectizer v.5.5.23: enveloped messages. This feature allows implementing revocable messages: messages/signals that can be revoked during delivery.

Ordinary SObjectizer's messages/signals are not revocable. Once an user calls send():

so_5::send<my_message>(target, ...);

the delivery of my_message instance can't be affected by the sender.

Sometimes a message sent should be revoked. For example, we can have session_handler agent that handles interaction with a user. And we can have pdf_generator agent that performs CPU-intensive operations of the generation of PDF-file from user-specific data. Agent session_handler sends a generate_pdf message to pdf_generator and then expects a reply with generated PDF-file.

But session_handler can detect that the user closed session. In that case, all previous generate_pdf messages have to be revoked. There is no need to handle them and waste pdf_generator resources. But we can't do that if we are using ordinary SObjectizer messages.

The new stuff from so_5::extra::revocable_msg allows solving that problem. We can send generate_pdf as an revocable message and we can revoke message sent when a user closes the session.

Revocation Doesn't Mean Extraction Of A Message From All Queues

Revocation of message doesn't mean that message will be extracted from all event queues at the moment of revocation. It is simply impossible.

Revocable delivery is implemented via a special envelope. And that envelope will stay inside all event queues until it will be extracted for processing the usual way. But if the envelope is revoked it won't be handled.

Revocation Is Impossible If Message Was Transformed

If a revocable message is transformed during delivery (as part of limit_then_transform overlimit reaction or because message was collected by collecting mbox) then transformed message can't be revoked. Transformed message regarded as a new one, that has no relation to original revocable message.

Header File

All stuff related to revocable messages are defined in so_5_extra/revocable_msg/pub.hpp header file. So to use this functionality it is necessary to include that file and so_5/all.hpp file:

#include <so_5_extra/revocable_msg/pub.hpp>

#include <so_5/all.hpp>

How To Send And Revoke A Message Or Signal?

To send a revocable message or signal it is necessary to use a send function from so_5::extra::revocable_msg and to store the delivery_id_t instance returned. For example:

class session_handler final : public so_5::agent_t {
    ...
    // ID of revocable message.
    so_5::extra::revocable_msg::delivery_id_t current_pdf_request_;
    ...
    void on_generate_document_cmd(mhood_t<generate_doc_cmd> cmd) {
        if(doc_type::pdf == cmd->doc_type()) {
            // Send a request to pdf_generator.
            current_pdf_request_ = so_5::extra::revocable_msg::send<generate_pdf>(...);
            ...
        }
        else ...
    }
    ...
};

To revoke message/signal sent these ways can be used:

  1. Destruction of delivery_id_t object. Destructor of delivery_id_t automatically revokes message/signal sent.
  2. Calling delivery_id_t::revoke() method. This method revokes message/signal sent. It is safe to call revoke() several times.
  3. Assigning a new value to delivery_id_t object. Previously sent message/signal is revoked in this case.

For example:

class session_handler final : public so_5::agent_t {
    ...
    // ID of revocable message.
    // Last sent generate_pdf message will be remoked automatically
    // during destruction of session_handler.
    so_5::extra::revocable_msg::delivery_id_t current_pdf_request_;
    ...
    void on_generate_document_cmd(mhood_t<generate_doc_cmd> cmd) {
        if(doc_type::pdf == cmd->doc_type()) {
            // Send a request to pdf_generator.
            // Previously sent message will be revoked automatically.
            current_pdf_request_ = so_5::extra::revocable_msg::send<generate_pdf>(...);
            ...
        }
        else ...
    }
    ...
    void on_cancel_document_generation(mhood_t<cancel_generate_doc_cmd> cmd) {
        // Last sent generate_pdf will be revoked.
        current_pdf_request_.revoke();
        ...
    }
};

Several Forms Of send() Functions

There are several forms of send() functions in so_5::extra::revocable_msg namespace. They are mimic send() functions from main so_5 namespace.

The first form constructs a new message instance and sends it to mbox, mchain or to a direct mbox of agent/ad-hoc agent:

auto id = so_5::extra::revocable_msg::send<my_message>(destination,
    ... /* Arguments for my_message's constructor */
    );

The second form allows to resend an existing message/signal as a revocable one:

void on_some_event(mhood_t<some_msg> cmd) {
    auto id = so_5::extra::revocable_msg::send(destination, std::move(cmd));
}

Revocable Delayed And Periodic Messages

There is no send_delayed nor send_periodic functions in so_5::extra::revocable_msg namespace. Revocable timers are implemented in so_5::extra::revocable_timer namespace.

Some Technical Details

Envelope

A message/signal that sent via so_5::extra::revocable_msg::send() function is enveloped into a special envelope with atomic revocation flag inside. A smart pointer to that envelope is stored inside delivery_id_t object. That smart pointer is released when the message is revoked.

The revocation means setting a special flag value inside the envelope. The envelope checks that flag in access_hook() method and provides the access to enveloped message/signal only if that flag is not set.

Message Limits

Revocation of message/signal doesn't affect message limits. It means that send() increments message counter, but revoke() doesn't decrement it. Message counter will be decremented only on the extraction of an envelope with a message from an event queue.

Clone this wiki locally