Skip to content

Fix serializer recursion handling and bump version to 1.0.3#40

Open
karle0wne wants to merge 4 commits intomasterfrom
stackoveflow
Open

Fix serializer recursion handling and bump version to 1.0.3#40
karle0wne wants to merge 4 commits intomasterfrom
stackoveflow

Conversation

@karle0wne
Copy link
Copy Markdown
Contributor

@karle0wne karle0wne commented Mar 24, 2026

Стабилизируем serializer и migrator в тех местах, где библиотека вела себя непредсказуемо на сложных данных. Несколько исправлений: циклические thrift-структуры, рекурсивные схемы при генерации mock-объектов, передача данных через цепочку миграций и разбор некорректного входа в разных представлениях. Раньше такие случаи могли заканчиваться переполнением стека, бесконечным обходом структуры или просто неверной обработкой данных, поэтому задача этого патча именно в том, чтобы сделать поведение явным и безопасным.

В TBaseProcessor добавлена проверка на зацикленные ссылки, в MockTBaseProcessor такая же защита появилась для рекурсивных thrift-схем, в BaseMigrationManager исправлена логика последовательной миграции, чтобы каждый следующий шаг получал уже преобразованный результат предыдущего шага. Заодно приведена в порядок обработка отдельных пограничных случаев в ObjectProcessor, JsonProcessor и XMLProcessor: исправлено чтение ByteBuffer, убран ошибочный проход мимо ветки SET, и для неизвестных типов библиотека теперь сразу сообщает об ошибке понятным исключением.

@karle0wne karle0wne requested a review from a team as a code owner March 24, 2026 10:16
Comment on lines +102 to +113
private static class AccumulatingMigrator implements Migrator {

@Override
public <I, O> O migrate(I data, MigrationPoint mPoint, SerializerSpec<I, O> serializerSpec) {
return (O) (String.valueOf(data) + ((MigrationSpecImpl) mPoint.getMigrationSpec()).getSpec().replace("TEST", ""));
}

@Override
public String getMigrationType() {
return "TEST";
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Добавь контекста, что проверяется, в стиле Панкрашкина ничего не понять

Copy link
Copy Markdown
Contributor Author

@karle0wne karle0wne Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

добавил комментов к тестам и названия тестов, тут суть что у нас есть способ в геке мигрировать трифт , но был баг что он брал промежуточный результат предыдущего шага всегда исходное и миграция по сути не работала и тесты проверяют работу BaseMigrationManager

private ValueGenerator valueGenerator;

private Map<String, FieldHandler> fieldHandlers = new HashMap<>();
private final Deque<Class<? extends TBase>> structPath = new ArrayDeque<>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему выбрал Deque?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

поправил на сет , первоначально попал сюда как идея хранить путь рекурсии , но тк нам тут нужна только проверка встречался ли тип в текущей ветки или нет и этого хватит и через сет


private void processStruct(StructMetaData structMetaData, StructHandler handler) throws IOException {
Class<? extends TBase> structClass = structMetaData.getStructClass();
if (structPath.contains(structClass)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Правильно понимаю, что мы просто StackOv меняем на другое исключение, мб мы хотим просто пропустить ее и догенерить то что можем? Только ли это используется в тестах или может сломать и основные процессы?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, подробнее расписал в шапке ответа , затрагивает и TBaseProcessor.java и MockTBaseProcessor.java , то есть и основу

Comment on lines +123 to +126
ByteBuffer duplicate = ((ByteBuffer) value).duplicate();
byte[] bytes = new byte[duplicate.remaining()];
duplicate.get(bytes);
handler.value(bytes);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Какой то набор приведений, поясни что тут было сделано и зачем

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Какой то набор приведений, поясни что тут было сделано и зачем

Лучше не следовать стилю Панкрашкина, у него ничего не понятно он сишник

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

поправил . цель корректно обработать ByteBuffer при сериализации

} catch (InstantiationException | IllegalAccessException ex) {
throw new IOException(ex);
} finally {
structPath.pop();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Точно ли нужно создавать Deque для того чтобы проверить что новый объект который тут же создается и тот который пришел будут одного типа, на сколько я понял логику? почему бы просто это и не проверить, для чего структура на уровне корня объекта с такой сложной логикой?

Copy link
Copy Markdown
Contributor Author

@karle0wne karle0wne Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, не нужен , выше написал, нужна только проверка и ииспользуем сет . структура хранения не локальна потому что отлавливаем граф как A -> A так и A -> B -> A в данной ветке , нужен для всей ветки обхода, но обернут в ThreadLocal для изоляции и живет в контексте одного запуска process


@Override
public boolean equals(Object other) {
return this == other;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему такой метод equels?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ошибка, сделал RecursiveStruct

return META_DATA_MAP;
}

public enum _Fields implements TFieldIdEnum {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему тут еще и enum прям в тесте описан? Для чего он нужен

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вынес RecursiveStruct отдельно, TBase требует описание трифт полей, в код гене также

}

public static _Fields findByThriftId(int fieldId) {
return fieldId == 1 ? NEXT : null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

что делает этот метод?

Copy link
Copy Markdown
Contributor Author

@karle0wne karle0wne Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

маппинг в энам для fieldForId , это все имитация какого то интересного трифт объекта с рекурсией

Comment on lines +86 to +97
BinaryTest binaryTest = new BinaryTest();
ByteBuffer buffer = ByteBuffer.wrap(new byte[]{9, 1, 2, 8});
buffer.position(1);
buffer.limit(3);

binaryTest.setData(buffer);
binaryTest.setDataInList(Collections.emptyList());
binaryTest.setDataInSet(Collections.emptySet());
binaryTest.setDataInMap(Collections.emptyMap());

BinaryCapturingHandler handler = new BinaryCapturingHandler();
new TBaseProcessor().process(binaryTest, handler);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

что тут проверяется? и для чего

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это тест по багу с ByteBuffer , смотрим что в сериализацию не попадает мусор

);
}

private static class BinaryCapturingHandler implements StructHandler<byte[]> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

еще какой-то страшный класс внутри теста, точно нужен тут?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вынес

@strug
Copy link
Copy Markdown

strug commented Mar 25, 2026

Стабилизируем serializer и migrator в тех местах, где библиотека вела себя непредсказуемо на сложных данных. Несколько исправлений: циклические thrift-структуры, рекурсивные схемы при генерации mock-объектов, передача данных через цепочку миграций и разбор некорректного входа в разных представлениях. Раньше такие случаи могли заканчиваться переполнением стека, бесконечным обходом структуры или просто неверной обработкой данных, поэтому задача этого патча именно в том, чтобы сделать поведение явным и безопасным.

В TBaseProcessor добавлена проверка на зацикленные ссылки, в MockTBaseProcessor такая же защита появилась для рекурсивных thrift-схем, в BaseMigrationManager исправлена логика последовательной миграции, чтобы каждый следующий шаг получал уже преобразованный результат предыдущего шага. Заодно приведена в порядок обработка отдельных пограничных случаев в ObjectProcessor, JsonProcessor и XMLProcessor: исправлено чтение ByteBuffer, убран ошибочный проход мимо ветки SET, и для неизвестных типов библиотека теперь сразу сообщает об ошибке понятным исключением.

Напоминает результат работы ИИ, в кратце что хотели сделать?

Copy link
Copy Markdown
Contributor Author

@karle0wne karle0wne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... хотели ...

Да, без водкии в геке не разбраться.
в корневом описании хотел показать основные точки правок, но попробую еще раз сформулировать

TBaseProcessor.java MockTBaseProcessor.java могут уходить в бесконечную рекурсию и выдавать StackOverflowError и этот патч не решает корень этой проблемы (далее объясню) а делает явно перехват сигнатур похожих на рекурсию и выкидывает исключения , помимо этого по мелочам чистит баги, также тесты которые проверяют что эти кейсы закрылись

не может решить проблему в корне потому что сам гек имеет архитектурные ограничения (трифт джава ген это граф, json это дерево), чтоб это решить нужно вводить доп структуры где хранятся референсы на объекты которые проходили (как то подружить дерево и граф) , и судя по всему для этого нужно было бы сделать чуть ли не гек в2 , что не целесообразно. потому что там много форматов msdpack, xml, json и для каждого надо отдельно прорабатывать , поэтому остановились пока на такой небольшой доработки

то есть если резюмировать план работ

  • перехват рекурсий
  • баг с ByteBuffer когда нужен захват только активной части - он мог сериализовывать лишние байты (есть тест на эту ситуацию)
  • баг когда не останавливалось выполенение в ObjectProcessor.java когда нашел set
  • баг BaseMigrationManager.java что он не получал следующий шаг в миграции трифт схемы а всегда обращался к исходным данным
  • тесты
  • по мелочи

@karle0wne karle0wne requested a review from strug March 26, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants