Как обеспечить, чтобы благотворительные объекты были обратно совместимы?
В настоящее время мы используем благотворительность для развития наших микро-услуг. Когда я недавно столкнулся с этой проблемой ниже.
Предположим ниже, что это экономичный контракт для объекта Summary, и есть API, который получает и обновляет сводку, используя переданный объект Summary.
Версия - 1.0
struct Summary {
1: required string summaryId,
2: required i32 summaryCost
}
Summary getSummary(1: string summaryId);
void updateSummary(1: Summary summary);
Теперь предположим, что есть 5 сервисов, которые используют этот контракт 1.0.
В следующем выпуске мы добавим еще один объект, называемый списком сводных значений.
Таким образом, новый контракт будет выглядеть
Версия - 2.0
struct Summary {
1: required string summaryId,
2: required i32 summaryCost,
3: optional list<i32> summaryValues
}
Summary getSummary(1: string summaryId);
void updateSummary(1: Summary summary);
- Поэтому, когда этот список ниже заполнен, мы сохраняем список значений
summaryValues
Аганист, которыйsummaryId
, - И когда клиент отправляет этот список как
null
мы удаляем существующие значения, сохраненные для этого summaryId.
Теперь проблема возникает, когда другие сервисы, использующие более старую версию контракта (версия 1.0), пытаются вызвать getSummary и updateSummary.
Намерение Старшего Клиента путем вызова updateSummary состояло в том, чтобы установить другое значение для summaryCost
, Однако, так как этот клиент не содержит объект summaryValues
он отправляет объект Summary с summaryValues
как ноль для сервера.
В результате Сервер удаляет все существующие значения summaryValues
для этого summaryId
,
Есть ли способ справиться с этим в бережливости? Методы isSet() здесь не работают, так как они пытаются выполнить простую нулевую проверку.
Каждый раз, когда мы выпускаем более новый клиент с модификацией существующих объектов, нам приходится принудительно обновлять версии клиентов других серверов, даже если это изменение не связано с ними.
1 ответ
В вашем version 2.0
контракт updateSummary()
метод изменился (т.е. теперь он позволяет сохранять и удалять итоговые значения):
Вариант 1. Вместо изменения его поведения создайте новый метод (т. Е. updateSummaryV2()
) и начните использовать его в последней версии ваших клиентов, но при этом не используйте старую.
Таким образом, старые версии клиента по-прежнему используют обычные updateSummary()
без противоречий с договором и допущениями нового метода.
Вариант 2. Добавьте необязательное поле, содержащее версию API и значение по умолчанию, установленное на последнюю версию API:
struct Summary {
1: required string summaryId,
2: required i32 summaryCost,
3: optional list<i32> summaryValues
4: optional i32 apiVersion = 2
}
Таким образом, если apiVersion
не задан, вы знаете, что запрос поступил от старого клиента, и для будущих версий вы будете знать версию клиента и можете реагировать соответствующим образом.
В качестве альтернативы вы можете удалить записи, только если предоставлен пустой список, и ничего не делать, если список не задан, чтобы соблюдать предыдущий контракт метода.
В качестве дополнительного примечания: наличие действия, зависящего от чего-то неявного (в данном случае, отсутствие списка), может быть в целом рискованным, даже без учета проблемы перекрестной совместимости. Как правило, безопаснее (и легче работать и поддерживать), когда такие действия зависят от явного флага.