real-logic / simple-binary-encoding

Simple Binary Encoding (SBE) - High Performance Message Codec
Apache License 2.0
3.12k stars 524 forks source link

[Java] DTOs to support encoding into direct buffer via instance method. #1023

Open the-thing opened 1 week ago

the-thing commented 1 week ago

Currently it is possible to encode DTO using the following methods:

These methods are static so using them can cause a lot of duplication. Example.

class Sender1
{
    private final RingBuffer publisher;

    Sender1(final RingBuffer publisher)
    {
        this.publisher = publisher;
    }

    public boolean send(final CarDto car)
    {
        final int encodedLength = car.computeEncodedLength();
        final int index = publisher.tryClaim(1, encodedLength);

        if (index <= 0)
        {
            return false;
        }

        final AtomicBuffer buffer = publisher.buffer();

        try
        {
            CarDto.encodeWith(car, buffer, index);
        }
        catch (final Exception e)
        {
            publisher.abort(index);
            return false;
        }

        publisher.commit(index);

        return true;
    }

    // crate new method and repeat the same for another DTO
    public void send(final FooDto foo)
    {
    }
}

Adding computeEncodedLengthwas great since now it makes easier to estimate the object size to be used with io.aeron.Publication#tryClaim or org.agrona.concurrent.ringbuffer.RingBuffer#tryClaim.

Can we create a common interface such as e.g.:

public interface DtoEncoder
{

    int encode(MutableDirectBuffer buffer, int offset);

    int encodeWithHeader(MutableDirectBuffer buffer, int offset);

    // implementation already available 
    int computeEncodedLength();

    int computeEncodedLengthWithHeader();
}

that generates DTO code similar to:

public final class CarDto implements DtoEncoder
{
    @Override
    public int encode(final MutableDirectBuffer buffer, final int offset)
    {
        return encodeWith(this, buffer, offset);
    }

    @Override
    public int encodeWithHeader(final MutableDirectBuffer buffer, final int offset)
    {
        return encodeWithHeaderWith(this, buffer, offset);
    }

    @Override
    public int computeEncodedLengthWithHeader()
    {
        return MessageHeaderEncoder.ENCODED_LENGTH + computeEncodedLength();
    }
}

This would simplify encoding so all DTOs can be serialized into a direct buffer with a common interface.

class Sender2 {

    private static final int MSG_TYPE = 1;
    private final RingBuffer publisher;

    public Sender2(RingBuffer publisher) {
        this.publisher = publisher;
    }

    // can be used for all DTOs
    public boolean send(DtoEncoder encoder) {
        int encodedLength = encoder.computeEncodedLength();
        int index = publisher.tryClaim(MSG_TYPE, encodedLength);

        if (index <= 0) {
            return false;
        }

        AtomicBuffer buffer = publisher.buffer();

        try {
            encoder.encode(buffer, index);
        } catch (Exception e) {
            publisher.abort(index);
            return false;
        }

        publisher.commit(index);

        return true;
    }
}

I have a prototype for this change, but I this would require creating a common interface for DTOs in Agrona org.agrona.sbe. Let me know what are your thoughts.