← На главную

Celly: 2456 тестов cel-spec, быстрее cel-go и protovalidate для .NET

27.07.2026 20:01 · hackernews

Появилась Celly — чистая C#-реализация Google Common Expression Language (CEL), которая с нуля проходит всю официальную conformance-батарею. Попутно из этого проекта выросла первая библиотека protovalidate для .NET. Всё уже выложено на NuGet.

CEL — небольшой безопасный язык выражений от Google, на котором держатся правила准入 Kubernetes, RBAC в Envoy, условия Cloud IAM и проверки protobuf через protovalidate. Идея в том, чтобы логика вроде request.auth.groups.exists(g, g == "admin") || resource.owner == request.auth.subject жила как данные в конфигах и вычислялась без права запустить произвольный код. Язык намеренно не тьюринг-полный — никаких бесконечных циклов, рекурсии и побочных эффектов, только типизированная обработка переданных данных. Поменять правило можно за секунды без пересборки сервиса, а одно и то же выражение работает одинаково в Go, C++, Java, Rust и теперь в .NET.

Официальных реализаций под .NET не было. Существовали частичные порты, не проходившие тесты на совместимость. Автор решил написать движок с жёстким ограничением: только чистый управляемый C#, без компиляции Go-кода в WASM и без нативных привязок. Лексер, рекурсивный парсер, типизация и вычислятель написаны с нуля.

Чтобы подтвердить совместимость, проект с первого коммита подключили к официальному набору из 2456 тестов cel-spec. Использовали ratcheting skip-list: упавший тест можно было временно игнорировать, но как только он начинал проходить, сборка падала, пока его не убирали из списка пропусков. Сегодня список пуст — 2456 из 2456. Дополнительно автор проверил всё через отдельный аудит с другой конвертацией тестовых данных и дифференциальный фаззер, который генерирует десятки тысяч случайных выражений и сравнивает результат Celly с эталонным cel-go побайтово. Расхождений нет.

CEL-правила компилируются один раз в неизменяемый потокобезопасный CelProgram, а затем вычисляются на каждом запросе. В тестах Celly оказалась быстрейшей CEL-библиотекой под .NET — примерно в 1.2 раза шустрее ближайшего управляемого аналога и в разы быстрее остальных. На выражениях с активным использованием map и filter она обгоняет даже cel-go (~15 мкс против ~22 мкс на 100 элементах). Секрет в двух решениях: во-первых, все расширения встраиваются в тот же обход дерева, что и встроенные функции, без дополнительных накладных расходов, поэтому на выражениях с большим числом extension-функций Celly примерно вдвое быстрее cel-go. Во-вторых, для списковых аккумуляторов в comprehensions применён rope-подход с амортизированным O(1) на добавление, вместо копирования всего списка за O(n²). Загрузка библиотек происходит на этапе компиляции, и время вычисления простого выражения (~125 нс) не зависит от числа подключенных библиотек.

Из побочного эффекта получилась первая реализация protovalidate для .NET. protovalidate — наследник protoc-gen-validate от buf — описывает правила валидации protobuf-сообщений прямо в .proto-файлах. Сами правила написаны на CEL, поэтому, уже имея conformant-движок, оставалось лишь читать их через рефлексию и вычислять. Это вылилось в прохождение ещё 2872 конформных тестов от buf. Библиотека упакована в NuGet-пакет Celly.Protovalidate. Попробовать можно командами dotnet add package Celly, Celly.Protobuf, Celly.Protovalidate.

Читать оригинал →