From 96f4d03bacbde39bf27f17e425f84ea9d0a684e8 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Wed, 1 Oct 2025 10:51:47 -0400 Subject: [PATCH 1/4] Add RFC for plugin connectors --- RFC-0016-connector-plugins.md | 228 ++++++ RFC-0016/architecture-diagram.excalidraw | 888 +++++++++++++++++++++++ RFC-0016/architecture-diagram.png | Bin 0 -> 65923 bytes 3 files changed, 1116 insertions(+) create mode 100644 RFC-0016-connector-plugins.md create mode 100644 RFC-0016/architecture-diagram.excalidraw create mode 100644 RFC-0016/architecture-diagram.png diff --git a/RFC-0016-connector-plugins.md b/RFC-0016-connector-plugins.md new file mode 100644 index 00000000..0e227e50 --- /dev/null +++ b/RFC-0016-connector-plugins.md @@ -0,0 +1,228 @@ +# RFC-0016 for Presto + +## Connector Plugin Support for Presto C++ Workers + +Proposers +* Tim Meehan + +## Related Issues + +* [RFC-0013](./RFC-0013-dyllib-cpp.md) - Introduced the `registerExtensions` mechanism for dynamically loaded functions, which this RFC extends for connectors +* [RFC #38](https://github.com/prestodb/rfcs/pull/38) - Introduces the Java and C++ codec infrastructure for connector-provided serialization in the context of Thrift serialization for coordinator to worker communication +* [PR #26026](https://github.com/prestodb/presto/pull/26026) - Builds on RFC #38 to enable binary serialization codecs for JSON protocol (avoiding Thrift migration) and adds missing codecs embedded in TaskUpdateRequest + +## Summary + +Enable runtime connector registration in Presto C++ workers through connector plugins loaded as shared libraries using protocol-agnostic binary serialization. This allows developers to use the existing `registerExtensions` mechanism to add custom Velox connectors while providing the ability to decode Java-side connector structures into corresponding Velox structures. + +## Background + +Presto C++ workers require all connectors at compile time. The protocol generation statically creates C++ classes from Java definitions, requiring connector-specific data structures in the worker binary. This prevents development of new connectors not known to the Presto engine, representing a significant regression from Java's functionality. + +Java coordinators support connector plugin registration with both JSON and Thrift protocols. [PR #26026](https://github.com/prestodb/presto/pull/26026)[^1] adds binary serialization codec support to the JSON protocol. + +### Goals + +* Enable runtime connector registration in C++ workers + +### Non-goals + +* Cross-language plugin support (Rust, Go) +* Hot-reload capabilities + +## Proposed Implementation + +### Modules Involved + +1. **presto-native-execution** + +### Components + +This RFC leverages existing infrastructure: +* **ConnectorProtocol**: Existing interface that plugins implement with binary serialization for their connector types[^2] +* **PrestoToVeloxConnector**: Existing class that creates the ConnectorProtocol and converts protocol types to Velox types +* **registerExtensions**: Existing mechanism from RFC-0013 used to register connector components from shared libraries + +### Implementation Flow + +1. **Plugin Registration** (using existing `registerExtensions` mechanism): + ```cpp + extern "C" void registerExtensions() { + // Register Presto-to-Velox converter (which creates the protocol) + auto converter = std::make_unique(); + registerPrestoToVeloxConnector("my-connector", std::move(converter)); + + // Register Velox connector factory + velox::connector::registerConnectorFactory("my-connector", factory); + } + ``` + +2. **Message Routing**: + - Coordinator includes connector name in the serialized payload (embedded in handle objects) + - Worker extracts connector name from the deserialized handle to identify which ConnectorProtocol to use + - ConnectorManager maintains registry mapping connector names to their protocols: + * Static connectors → pre-compiled protocol classes registered at startup + * Plugin connectors → ConnectorProtocol implementations from shared libraries + +3. **Binary Serialization Protocol**[^3]: + - JSON (via [PR #26026](https://github.com/prestodb/presto/pull/26026)) and Thrift support binary payloads + - ConnectorCodecProvider (Java) ↔ ConnectorProtocol (C++) + +4. **C++ Worker Configuration**[^4]: + ```properties + # config.properties for C++ worker + plugin.dir=/opt/presto/plugin # Optional, defaults to ./plugin + ``` + Worker scans plugin directory at startup and loads all shared libraries (.so/.dylib files) using dlopen. + +### Architecture Diagram + +![Architecture Diagram](RFC-0016/architecture-diagram.png) + +### Interface Contracts + +Each plugin provides its own ConnectorProtocol implementation with connector-specific binary serialization. Worker loads plugins using the mechanism introduced in [RFC-0013](./RFC-0013-dyllib-cpp.md) (and documented [here](https://prestodb.io/docs/current/presto_cpp/plugin/function_plugin.html)), where shared libraries are loaded via dlopen/dlsym at startup based on configuration. + +## Metrics + +* N/A + +## Other Approaches Considered + +N/A + +## Adoption Plan + +* **Impact**: None on existing users. Static connectors unchanged. +* **New configurations**: `plugin.dir` property in C++ worker config.properties +* **Migration**: Gradual per-connector basis +* **Documentation**: Plugin development guide, example implementations +* **Out of scope**: Cross-language support, hot-reload + +## Test Plan + +1. **Unit tests**: + - ConnectorProtocol serialization correctness + - Plugin loading/unloading + - Error handling + +2. **Integration tests**: + - End-to-end queries with plugin connectors + - Mixed static/plugin queries + - Failure scenarios + +## Implementation Considerations + +### Security + +Shared libraries must be trusted code (same as Java plugins). + +### Performance + +Virtual function overhead negligible compared to network I/O. + +### Compatibility + +Plugins require same toolchain as worker binary for ABI compatibility. The Java coordinator must enable `use-connector-provided-serialization-codecs` and have a complete implementation of the binary codec for the connector. Both coordinator and worker need matching SerializationCodec implementations. + +### Limitations + +C++ ABI compatibility requires matching compiler versions and platform. + +[^1]: [PR #26026](https://github.com/prestodb/presto/pull/26026) enables binary serialization in JSON protocol via ConnectorCodecProvider. + +[^2]: ConnectorProtocol interface that plugins implement: +```cpp +class ConnectorProtocol { +public: + // Plugins implement these for binary serialization + virtual void serialize(const std::shared_ptr& proto, + std::string& binary) const = 0; + virtual void deserialize(const std::string& binary, + std::shared_ptr& proto) const = 0; + + // JSON/Thrift methods + virtual void to_json(json& j, const std::shared_ptr& p) const; + virtual void from_json(const json& j, std::shared_ptr& p) const; +}; +``` + +[^3]: Protocol extensions: +```thrift +struct PluginConnectorData { + 1: required string connectorName + 2: required binary serializedData + 3: optional map metadata + 4: required i32 protocolVersion +} + +union ConnectorSplitData { + 1: HiveSplit hiveSplit + 2: IcebergSplit icebergSplit + 3: PluginConnectorData pluginData +} +``` +JSON with binary payload: +```json +{ + "@type": "ConnectorSplit", + "connectorId": "my-connector", + "serializedData": "", + "metadata": {"protocolVersion": "1"} +} +``` + +[^4]: C++ worker configuration and plugin example: +```properties +# config.properties for C++ worker +plugin.dir=/opt/presto/plugin # Optional, defaults to ./plugin +``` +Example plugin: +```cpp +// Step 1: Protocol deserialization - handles binary to protocol type +class ArrowFlightProtocol : public ConnectorProtocol { + void deserialize(const std::string& binary, + std::shared_ptr& split) const override { + // Deserialize binary into protocol-specific type + ArrowFlightSplit protocolSplit; + // ... deserialize binary data into protocolSplit ... + split = std::make_shared(std::move(protocolSplit)); + } + + void serialize(const std::shared_ptr& split, + std::string& binary) const override { + auto arrowSplit = std::dynamic_pointer_cast(split); + // ... serialize arrowSplit to binary ... + } +}; + +// Step 2: PrestoToVeloxConnector - creates protocol and converts types +class ArrowFlightPrestoToVeloxConnector : public PrestoToVeloxConnector { +public: + std::unique_ptr createConnectorProtocol() const override { + return std::make_unique(); + } + + std::unique_ptr toVeloxSplit( + const std::shared_ptr& prestoSplit) const override { + auto arrowSplit = std::dynamic_pointer_cast(prestoSplit); + // Convert protocol ArrowFlightSplit to Velox ArrowFlightConnectorSplit + return std::make_unique( + arrowSplit->ticket, + arrowSplit->endpoint); + } +}; + +extern "C" { + void registerExtensions() { + // Register Presto-to-Velox converter + auto converter = std::make_unique(); + registerPrestoToVeloxConnector("arrow-flight", std::move(converter)); + + // Register Velox connector factory + velox::connector::registerConnectorFactory( + "arrow-flight", + std::make_unique()); + } +} +``` \ No newline at end of file diff --git a/RFC-0016/architecture-diagram.excalidraw b/RFC-0016/architecture-diagram.excalidraw new file mode 100644 index 00000000..b6a89ee3 --- /dev/null +++ b/RFC-0016/architecture-diagram.excalidraw @@ -0,0 +1,888 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw-jetbrains-plugin", + "elements": [ + { + "type": "rectangle", + "version": 4, + "versionNonce": 1970594961, + "isDeleted": false, + "id": "coordinator-box", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 100, + "y": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 600, + "height": 150, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "a0" + }, + { + "type": "text", + "version": 57, + "versionNonce": 1389178961, + "isDeleted": false, + "id": "coordinator-label", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 306.0300750732422, + "y": 69.87890625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 187.93984985351562, + "height": 25, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327051278, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Coordinator (Java)", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Coordinator (Java)", + "lineHeight": 1.25, + "baseline": 18, + "index": "a1", + "autoResize": true + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 1735142001, + "isDeleted": false, + "id": "codec-provider-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 150, + "y": 120, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "width": 500, + "height": 60, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "a2" + }, + { + "type": "text", + "version": 6, + "versionNonce": 1714987217, + "isDeleted": false, + "id": "codec-provider-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 222.3361053466797, + "y": 140, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 355.3277893066406, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327050163, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "ConnectorCodecProvider (Binary Serialization)", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "ConnectorCodecProvider (Binary Serialization)", + "lineHeight": 1.25, + "baseline": 14, + "index": "a3", + "autoResize": true + }, + { + "type": "arrow", + "version": 4, + "versionNonce": 171947089, + "isDeleted": false, + "id": "transport-arrow", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 400, + "y": 200, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 80, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 80 + ] + ], + "index": "a4" + }, + { + "type": "text", + "version": 5, + "versionNonce": 816578353, + "isDeleted": false, + "id": "transport-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 420, + "y": 230, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 171.087890625, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327048393, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "JSON/Thrift Protocol", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "JSON/Thrift Protocol", + "lineHeight": 1.25, + "baseline": 14, + "index": "a5", + "autoResize": true + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 29819441, + "isDeleted": false, + "id": "worker-box", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 100, + "y": 300, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "width": 600, + "height": 400, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "a6" + }, + { + "type": "text", + "version": 47, + "versionNonce": 601046527, + "isDeleted": false, + "id": "worker-label", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 336.4611358642578, + "y": 319.87109375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 126.81991577148438, + "height": 25, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327053340, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Worker (C++)", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Worker (C++)", + "lineHeight": 1.25, + "baseline": 18, + "index": "a7", + "autoResize": true + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 183079953, + "isDeleted": false, + "id": "protocol-registry-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 150, + "y": 360, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "width": 500, + "height": 120, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "a8" + }, + { + "type": "text", + "version": 22, + "versionNonce": 1359836543, + "isDeleted": false, + "id": "protocol-registry-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 331.7200469970703, + "y": 370, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 136.55990600585938, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327052578, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Protocol Registry", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Protocol Registry", + "lineHeight": 1.25, + "baseline": 14, + "index": "a9", + "autoResize": true + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 1503586801, + "isDeleted": false, + "id": "hive-protocol-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 170, + "y": 410, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aA" + }, + { + "type": "text", + "version": 6, + "versionNonce": 1703042225, + "isDeleted": false, + "id": "hive-protocol-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 192.61602783203125, + "y": 425, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 94.7679443359375, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327055628, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "HiveProtocol", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "HiveProtocol", + "lineHeight": 1.25, + "baseline": 14, + "index": "aB", + "autoResize": true + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 921600977, + "isDeleted": false, + "id": "iceberg-protocol-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 330, + "y": 410, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aC" + }, + { + "type": "text", + "version": 6, + "versionNonce": 482989855, + "isDeleted": false, + "id": "iceberg-protocol-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 338.696044921875, + "y": 425, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 122.60791015625, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327056476, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "IcebergProtocol", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "IcebergProtocol", + "lineHeight": 1.25, + "baseline": 14, + "index": "aD", + "autoResize": true + }, + { + "type": "rectangle", + "version": 2, + "versionNonce": 1592779185, + "isDeleted": false, + "id": "plugin-protocol-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 490, + "y": 410, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aE" + }, + { + "type": "text", + "version": 3, + "versionNonce": 309719007, + "isDeleted": false, + "id": "plugin-protocol-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 505.90403747558594, + "y": 425, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 108.19192504882812, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327042883, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "PluginProtocol", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "PluginProtocol", + "lineHeight": 1.25, + "baseline": 14, + "index": "aF", + "autoResize": true + }, + { + "type": "arrow", + "version": 2, + "versionNonce": 516588433, + "isDeleted": false, + "id": "plugin-to-plugins-arrow", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 560, + "y": 460, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 40, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0, + 40 + ] + ], + "index": "aG" + }, + { + "type": "rectangle", + "version": 4, + "versionNonce": 1807612415, + "isDeleted": false, + "id": "plugins-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 150, + "y": 520, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "width": 500, + "height": 120, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aH" + }, + { + "type": "text", + "version": 78, + "versionNonce": 724616831, + "isDeleted": false, + "id": "plugins-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 311.3920593261719, + "y": 529.32421875, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.21588134765625, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327054496, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Connector Plugins (.so)", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Connector Plugins (.so)", + "lineHeight": 1.25, + "baseline": 14, + "index": "aI", + "autoResize": true + }, + { + "type": "rectangle", + "version": 2, + "versionNonce": 1795973663, + "isDeleted": false, + "id": "plugin-a-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 170, + "y": 570, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aJ" + }, + { + "type": "text", + "version": 3, + "versionNonce": 320201008, + "isDeleted": false, + "id": "plugin-a-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 209.3200225830078, + "y": 585, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 61.359954833984375, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327108694, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Plugin A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Plugin A", + "lineHeight": 1.25, + "baseline": 14, + "index": "aK", + "autoResize": true + }, + { + "type": "rectangle", + "version": 2, + "versionNonce": 546883135, + "isDeleted": false, + "id": "plugin-b-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 330, + "y": 570, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aL" + }, + { + "type": "text", + "version": 3, + "versionNonce": 983042512, + "isDeleted": false, + "id": "plugin-b-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 368.7520217895508, + "y": 585, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.49595642089844, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327108694, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Plugin B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Plugin B", + "lineHeight": 1.25, + "baseline": 14, + "index": "aM", + "autoResize": true + }, + { + "type": "rectangle", + "version": 2, + "versionNonce": 188982879, + "isDeleted": false, + "id": "plugin-c-box", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 490, + "y": 570, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 140, + "height": 50, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1759327036209, + "link": null, + "locked": false, + "index": "aN" + }, + { + "type": "text", + "version": 3, + "versionNonce": 1646410544, + "isDeleted": false, + "id": "plugin-c-label", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 529.4160232543945, + "y": 585, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 61.16795349121094, + "height": 20, + "seed": 1, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1759327108694, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Plugin C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Plugin C", + "lineHeight": 1.25, + "baseline": 14, + "index": "aO", + "autoResize": true + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/RFC-0016/architecture-diagram.png b/RFC-0016/architecture-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..947e44c6fcdbbe7df7305c54cee9f6ad629c46e8 GIT binary patch literal 65923 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׀Wg6? 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 }z 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 B9Yz2E literal 0 HcmV?d00001 From acb25533cc6c5806f5461c98e671b8edd9f1a0ff Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Wed, 22 Oct 2025 10:55:08 -0400 Subject: [PATCH 2/4] 16 -> 19 --- ...ctor-plugins.md => RFC-0019-connector-plugins.md | 4 ++-- .../architecture-diagram.excalidraw | 0 {RFC-0016 => RFC-0019}/architecture-diagram.png | Bin 3 files changed, 2 insertions(+), 2 deletions(-) rename RFC-0016-connector-plugins.md => RFC-0019-connector-plugins.md (99%) rename {RFC-0016 => RFC-0019}/architecture-diagram.excalidraw (100%) rename {RFC-0016 => RFC-0019}/architecture-diagram.png (100%) diff --git a/RFC-0016-connector-plugins.md b/RFC-0019-connector-plugins.md similarity index 99% rename from RFC-0016-connector-plugins.md rename to RFC-0019-connector-plugins.md index 0e227e50..bcd0ec5f 100644 --- a/RFC-0016-connector-plugins.md +++ b/RFC-0019-connector-plugins.md @@ -1,4 +1,4 @@ -# RFC-0016 for Presto +# RFC-0019 for Presto ## Connector Plugin Support for Presto C++ Workers @@ -77,7 +77,7 @@ This RFC leverages existing infrastructure: ### Architecture Diagram -![Architecture Diagram](RFC-0016/architecture-diagram.png) +![Architecture Diagram](RFC-0019/architecture-diagram.png) ### Interface Contracts diff --git a/RFC-0016/architecture-diagram.excalidraw b/RFC-0019/architecture-diagram.excalidraw similarity index 100% rename from RFC-0016/architecture-diagram.excalidraw rename to RFC-0019/architecture-diagram.excalidraw diff --git a/RFC-0016/architecture-diagram.png b/RFC-0019/architecture-diagram.png similarity index 100% rename from RFC-0016/architecture-diagram.png rename to RFC-0019/architecture-diagram.png From d3d90a28c27b2c30d780eceb3b5ff6b230d8b894 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Wed, 22 Oct 2025 11:11:01 -0400 Subject: [PATCH 3/4] Update code example --- RFC-0019-connector-plugins.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/RFC-0019-connector-plugins.md b/RFC-0019-connector-plugins.md index bcd0ec5f..d4f3cab8 100644 --- a/RFC-0019-connector-plugins.md +++ b/RFC-0019-connector-plugins.md @@ -180,20 +180,18 @@ plugin.dir=/opt/presto/plugin # Optional, defaults to ./plugin Example plugin: ```cpp // Step 1: Protocol deserialization - handles binary to protocol type -class ArrowFlightProtocol : public ConnectorProtocol { - void deserialize(const std::string& binary, - std::shared_ptr& split) const override { - // Deserialize binary into protocol-specific type - ArrowFlightSplit protocolSplit; - // ... deserialize binary data into protocolSplit ... - split = std::make_shared(std::move(protocolSplit)); +class TpchConnectorProtocol final : public ConnectorProtocol { + public: + void deserialize( + const std::string& binaryData, + std::shared_ptr& proto) const override; + + void to_json(json& j, const std::shared_ptr& p) + const override { + VELOX_NYI("JSON not supported with binary serialization"); } - void serialize(const std::shared_ptr& split, - std::string& binary) const override { - auto arrowSplit = std::dynamic_pointer_cast(split); - // ... serialize arrowSplit to binary ... - } + /* ... Other deserialization methods, serialization methods throw NYI errors ... */ }; // Step 2: PrestoToVeloxConnector - creates protocol and converts types From d815e7ed0101271701761edee0db7e515b1bb30c Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Thu, 23 Oct 2025 09:55:53 -0400 Subject: [PATCH 4/4] TPCH -> Arrow --- RFC-0019-connector-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0019-connector-plugins.md b/RFC-0019-connector-plugins.md index d4f3cab8..ac261928 100644 --- a/RFC-0019-connector-plugins.md +++ b/RFC-0019-connector-plugins.md @@ -180,7 +180,7 @@ plugin.dir=/opt/presto/plugin # Optional, defaults to ./plugin Example plugin: ```cpp // Step 1: Protocol deserialization - handles binary to protocol type -class TpchConnectorProtocol final : public ConnectorProtocol { +class ArrowConnectorProtocol final : public ConnectorProtocol { public: void deserialize( const std::string& binaryData,