Skip to content

Commit

Permalink
dylib presto changes + presto-docs + readme + examples
Browse files Browse the repository at this point in the history
  • Loading branch information
soumiiow committed Feb 11, 2025
1 parent 099bd42 commit 689b24e
Show file tree
Hide file tree
Showing 14 changed files with 326 additions and 0 deletions.
1 change: 1 addition & 0 deletions presto-docs/src/main/sphinx/presto-cpp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Note: Presto C++ is in active development. See :doc:`Limitations </presto_cpp/li

presto_cpp/features
presto_cpp/limitations
presto_cpp/plugin
presto_cpp/properties
presto_cpp/properties-session

Expand Down
21 changes: 21 additions & 0 deletions presto-docs/src/main/sphinx/presto_cpp/plugin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
*******************
Presto C++ Plugins
*******************

This chapter outlines the plugins in Presto C++ that are available for various use cases such as to load User Defined Functions (UDFs), connectors, or types.

.. toctree::
:maxdepth: 1

plugin/function_plugin


Setup
----------------

1. Place the plugin shared libraries in the ``plugins`` directory.

2. Set the ``plugin.dir`` property to the path of the ``plugins`` directory in the ``config.properties`` file of each of your workers.

3. Run the coordinator and workers to load your plugins.

38 changes: 38 additions & 0 deletions presto-docs/src/main/sphinx/presto_cpp/plugin/function_plugin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=======================
Function Plugin
=======================

Creating a Shared Library for UDFs
----------------------------------

1. To create the UDF, create a new C++ file which follows the following format:

.. code-block:: c++

#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"

template <typename T>
struct nameOfStruct {
FOLLY_ALWAYS_INLINE bool call(int64_t& result) {
...
}
};

extern "C" {
void registry() {
facebook::presto::registerPrestoFunction<
nameOfStruct,
int64_t>("function_name");
}
}

Note: the int64_t return type can be changed as needed. For more examples, See the `README <https://github.com/prestodb/presto-native-execution/main/dynamic_registry/README.md>`_

2. Create a shared library which may be made using CMakeLists like the following:

.. code-block:: text
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt Folly::folly gflags::gflags)
3. `Drop your shared libraries into the plugin directory <../plugin.rst>`_
12 changes: 12 additions & 0 deletions presto-native-execution/presto_cpp/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_subdirectory(types)
add_subdirectory(http)
add_subdirectory(common)
add_subdirectory(thrift)
add_subdirectory(dynamic_registry)

add_library(
presto_server_lib
Expand Down Expand Up @@ -62,9 +63,11 @@ target_link_libraries(
velox_dwio_orc_reader
velox_dwio_parquet_reader
velox_dwio_parquet_writer
velox_dynamic_library_loader
velox_encode
velox_exec
velox_file
velox_function_registry
velox_functions_lib
velox_functions_prestosql
velox_gcs
Expand Down Expand Up @@ -100,6 +103,15 @@ set_property(TARGET presto_server_lib PROPERTY JOB_POOL_LINK

add_executable(presto_server PrestoMain.cpp)

# The below additional flags are necessary for resolving dependencies for
# loading dynamic libraries.
if(APPLE)
target_link_options(presto_server BEFORE PUBLIC
"-Wl,-undefined,dynamic_lookup")
else()
target_link_options(presto_server BEFORE PUBLIC "-Wl,-export-dynamic")
endif()

# Moving velox_hive_connector and velox_tpch_connector to presto_server_lib
# results in multiple link errors similar to the one below only on GCC.
# "undefined reference to `vtable for velox::connector::tpch::TpchTableHandle`"
Expand Down
25 changes: 25 additions & 0 deletions presto-native-execution/presto_cpp/main/PrestoServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "velox/common/base/StatsReporter.h"
#include "velox/common/caching/CacheTTLController.h"
#include "velox/common/caching/SsdCache.h"
#include "velox/common/dynamic_registry/DynamicLibraryLoader.h"
#include "velox/common/file/FileSystems.h"
#include "velox/common/memory/MmapAllocator.h"
#include "velox/common/memory/SharedArbitrator.h"
Expand Down Expand Up @@ -396,6 +397,7 @@ void PrestoServer::run() {
registerRemoteFunctions();
registerVectorSerdes();
registerPrestoPlanNodeSerDe();
registerDynamicFunctions();

const auto numExchangeHttpClientIoThreads = std::max<size_t>(
systemConfig->exchangeHttpClientNumIoThreadsHwMultiplier() *
Expand Down Expand Up @@ -1598,5 +1600,28 @@ protocol::NodeStatus PrestoServer::fetchNodeStatus() {

return nodeStatus;
}
void PrestoServer::registerDynamicFunctions() {
auto systemConfig = SystemConfig::instance();
if (!systemConfig->pluginDir().empty()) {
// If user provided path is valid, traverse and call dynamic function loader
// for all shared libraries.
const fs::path path(systemConfig->pluginDir());
PRESTO_STARTUP_LOG(INFO) << path;
std::error_code ec;
if (fs::is_directory(path, ec)) {
using recursiveDirectoryIterator =
std::filesystem::recursive_directory_iterator;
std::set<std::string> extensions{".so", ".dylib"};
for (const auto& dirEntry : recursiveDirectoryIterator(path)) {
// Skip any non shared library files and directories from loading.
auto dirEntryPath = dirEntry.path();
if (!fs::is_directory(dirEntry, ec) &&
extensions.find(dirEntryPath.extension()) != extensions.end()) {
velox::loadDynamicLibrary(dirEntryPath.c_str());
}
}
}
}
}

} // namespace facebook::presto
2 changes: 2 additions & 0 deletions presto-native-execution/presto_cpp/main/PrestoServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ class PrestoServer {

VeloxPlanValidator* getVeloxPlanValidator();

virtual void registerDynamicFunctions();

/// Invoked to get the list of filters passed to the http server.
std::vector<std::unique_ptr<proxygen::RequestHandlerFactory>>
getHttpServerFilters();
Expand Down
5 changes: 5 additions & 0 deletions presto-native-execution/presto_cpp/main/common/Configs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ SystemConfig::SystemConfig() {
BOOL_PROP(kEnableRuntimeMetricsCollection, false),
BOOL_PROP(kPlanValidatorFailOnNestedLoopJoin, false),
STR_PROP(kPrestoDefaultNamespacePrefix, "presto.default"),
STR_PROP(kPluginDir, ""),
};
}

Expand Down Expand Up @@ -763,6 +764,10 @@ std::string SystemConfig::prestoDefaultNamespacePrefix() const {
return optionalProperty(kPrestoDefaultNamespacePrefix).value().append(".");
}

std::string SystemConfig::pluginDir() const {
return optionalProperty(kPluginDir).value();
}

NodeConfig::NodeConfig() {
registeredProps_ =
std::unordered_map<std::string, folly::Optional<std::string>>{
Expand Down
4 changes: 4 additions & 0 deletions presto-native-execution/presto_cpp/main/common/Configs.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,8 @@ class SystemConfig : public ConfigBase {
static constexpr std::string_view kInternalCommunicationJwtExpirationSeconds{
"internal-communication.jwt.expiration-seconds"};

/// Optional string containing the path to the plugin directory
static constexpr std::string_view kPluginDir{"plugin.dir"};
/// Below are the Presto properties from config.properties that get converted
/// to their velox counterparts in BaseVeloxQueryConfig and used solely from
/// BaseVeloxQueryConfig.
Expand Down Expand Up @@ -898,6 +900,8 @@ class SystemConfig : public ConfigBase {

bool prestoNativeSidecar() const;
std::string prestoDefaultNamespacePrefix() const;

std::string pluginDir() const;
};

/// Provides access to node properties defined in node.properties file.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include "presto_cpp/main/common/Configs.h"
#include "velox/functions/Macros.h"
#include "velox/functions/Registerer.h"
namespace facebook::presto {
template <template <class> class T, typename TReturn, typename... TArgs>
void registerPrestoFunction(
const char* name,
const char* nameSpace = "",
const std::vector<velox::exec::SignatureVariable>& constraints = {},
bool overwrite = true) {
std::string cpp_nameSpace(nameSpace);
if (cpp_nameSpace.empty()) {
auto systemConfig = SystemConfig::instance();
cpp_nameSpace = systemConfig->prestoDefaultNamespacePrefix();
}
std::string cpp_name(cpp_nameSpace);
cpp_name.append(name);
LOG(INFO) << "registering function: " << cpp_name;
facebook::velox::registerFunction<T, TReturn, TArgs...>(
{cpp_name}, constraints, overwrite);
}
} // namespace facebook::presto
20 changes: 20 additions & 0 deletions presto-native-execution/presto_cpp/main/dynamic_registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dynamic Loading of Presto CPP Extensions
This library adds the ability to load User Defined Functions (UDFs), connectors, or types without having to fork and build Prestissimo, through the use of shared libraries that a Prestissimo worker can access. These are loaded on launch of the Presto server. The Presto server searches for any .so or .dylib files and loads them using this library.
## Getting started
1. Create a cpp file for your dynamic library

For dynamically loaded function registration, the format is similar to that of built-in function registration, with some noted differences. Using [MyDynamicFunction.cpp](examples/MyDynamicFunction.cpp) as an example, the function uses the extern "C" keyword to protect against name mangling. A registry() function call is also necessary here.
2. Register functions dynamically by creating .dylib or .so shared libraries and dropping them in a plugin directory

These shared libraries may be made using CMakeLists like the following:
```
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt Folly::folly gflags::gflags)
```
3. In the Prestissimo worker's config.properties file, set the plugin.dir property

Set the value of plugin.dir to the file path of the directory where the shared libraries are located.
```
plugin.dir="User\Test\Path\plugin"
```
When the worker or the sidecar process starts, it scans the plugin directory and attempts to dynamically load all shared libraries.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

add_library(velox_function_my_dynamic SHARED MyDynamicFunction.cpp)
add_library(velox_varchar_function_my_dynamic SHARED
MyDynamicVarcharFunction.cpp)
add_library(velox_array_function_my_dynamic SHARED MyDynamicArrayFunction.cpp)

set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt gflags::gflags xsimd)
target_link_libraries(velox_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
target_link_libraries(velox_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
target_link_libraries(velox_array_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"
#include "velox/type/SimpleFunctionApi.h"

// This file defines a mock function that will be dynamically linked and
// registered. There are no restrictions as to how the function needs to be
// defined, but the library (.so) needs to provide a `void registry()` C
// function in the top-level namespace.
//
// (note the extern "C" directive to prevent the compiler from mangling the
// symbol name).

namespace facebook::velox::common::dynamicRegistry {

template <typename T>
struct Dynamic123Function {
VELOX_DEFINE_FUNCTION_TYPES(T);
FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Array<int64_t>>& array) {
result = array.size();
return true;
}
};
} // namespace facebook::velox::common::dynamicRegistry

extern "C" {
void registry() {
facebook::presto::registerPrestoFunction<
facebook::velox::common::dynamicRegistry::Dynamic123Function,
int64_t,
facebook::velox::Array<int64_t>>({"dynamic_1"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"

// This file defines a mock function that will be dynamically linked and
// registered. There are no restrictions as to how the function needs to be
// defined, but the library (.so) needs to provide a `void registry()` C
// function in the top-level namespace.
//
// (note the extern "C" directive to prevent the compiler from mangling the
// symbol name).

namespace facebook::velox::common::dynamicRegistry {

template <typename T>
struct Dynamic123Function {
FOLLY_ALWAYS_INLINE bool call(int64_t& result) {
result = 123;
return true;
}
};

} // namespace facebook::velox::common::dynamicRegistry

extern "C" {
// In this case, we assume that facebook::velox::registerFunction
// will be available and resolve when this library gets loaded.
void registry() {
facebook::presto::registerPrestoFunction<
facebook::velox::common::dynamicRegistry::Dynamic123Function,
int64_t>("dynamic_2");
}
}
Loading

0 comments on commit 689b24e

Please sign in to comment.