Мёртвая публичность

Памятка о protobuf(js)

В рамках рабочих задач познакомился с protobuf. Контекст такой: сериализованные сообщения пишутся в kafka-топик, а нам их надо читать и обрабатывать.

В связи с этим интересно, как эффективно и, не менее важно, лаконично работать с этим форматом в typescript + node.

Для интеграции использовалась библиотека protobufjs

Инициализация root

Очевидно, но можно случайно забыть.
Инициализация Root занимает время. Если частый encoding/decoding, нужно инициализировать единожды.

Хранение схемы

Есть два варианта:

Плюсы хранения proto-файлом:

Минусы хранения proto-файлом:

Плюсы хранения константой:

Минусы хранения константой:

Если инициализация происходит не часто, то хранение файлом выигрывает.

Опция keepCase

Преобразовывает поля из snake_case в схеме protobuf в camelCase.
По умолчанию true.

Плюсы:

На производительность явно не влияет, значение можно выбирать в зависимости от предпочтений.

Опция defaults

Protobuf позволяет не передавать значение для полей при сериализации.
Отсутствие значения = значение по умолчанию.

Defaults в значении true подставляет значения по умолчанию для не опциональных полей, если значение не было передано при сериализации.

Плюсы использования defaults: true:

Минусы:

По поводу производительности: явность позволяет не добавлять доп логику на проверку наличия полей, что в итоге может дать прирост с ростом бизнес-логики.
Я выбираю явность (defaults: true)

Опция: bytes: String

Добавление такой опции автоматически преобразует значения полей типа bytes в строку.
Проблема в том, что это преобразование в base64, что может оказаться неожиданностью. Лучше самостоятельно проебразовывать такие поля в необходимый тип.

Не использую

Опция: enum: String

Добавление такой опции автоматически преобразует значения полей типа enums в строку. Иначе значение остается int'ом.

На производительность явно не влияет, значение можно выбирать в зависимости от предпочтений.

Итого

import { Root, Type } from "protobufjs";

interface Input {
  filePath: string;
  type: string;
}

export class ProtobufMessageEncoder {
  private readonly type: Type;

  constructor({filePath, type}: Input) {
    const root = new Root();
    root.loadSync(filePath, {keepCase: false});
    this.type = root.lookupType(type);
  }

  public decode(value: Buffer | Uint8Array) {
    const message = this.type.decode(value);
    return this.type.toObject(message, {defaults: true});
  }
}