Skip to content

Commit 6cb96e7

Browse files
committed
dylib presto changes + presto-docs + readme + examples
1 parent 099bd42 commit 6cb96e7

File tree

15 files changed

+338
-0
lines changed

15 files changed

+338
-0
lines changed

presto-docs/src/main/sphinx/presto-cpp.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Note: Presto C++ is in active development. See :doc:`Limitations </presto_cpp/li
99

1010
presto_cpp/features
1111
presto_cpp/limitations
12+
presto_cpp/plugin
1213
presto_cpp/properties
1314
presto_cpp/properties-session
1415

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
*******************
2+
Presto C++ Plugins
3+
*******************
4+
5+
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.
6+
7+
.. toctree::
8+
:maxdepth: 1
9+
10+
plugin/function_plugin
11+
12+
13+
Setup
14+
----------------
15+
16+
1. Place the plugin shared libraries in the ``plugins`` directory.
17+
18+
2. Set the ``plugin.dir`` property to the path of the ``plugins`` directory in the ``config.properties`` file of each of your workers.
19+
20+
3. Run the coordinator and workers to load your plugins.
21+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=======================
2+
Function Plugin
3+
=======================
4+
5+
Creating a Shared Library for UDFs
6+
----------------------------------
7+
8+
1. To create the UDF, create a new C++ file which follows the following format:
9+
10+
.. code-block:: c++
11+
12+
#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"
13+
14+
template <typename T>
15+
struct nameOfStruct {
16+
FOLLY_ALWAYS_INLINE bool call(int64_t& result) {
17+
...
18+
}
19+
};
20+
21+
extern "C" {
22+
void registry() {
23+
facebook::presto::registerPrestoFunction<
24+
nameOfStruct,
25+
int64_t>("function_name");
26+
}
27+
}
28+
29+
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>`_
30+
31+
2. Create a shared library which may be made using CMakeLists like the following:
32+
33+
.. code-block:: text
34+
35+
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
36+
target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt Folly::folly gflags::gflags)
37+
38+
3. `Drop your shared libraries into the plugin directory <../plugin.rst>`_

presto-native-execution/presto_cpp/main/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_subdirectory(types)
1414
add_subdirectory(http)
1515
add_subdirectory(common)
1616
add_subdirectory(thrift)
17+
add_subdirectory(dynamic_registry)
1718

1819
add_library(
1920
presto_server_lib
@@ -62,9 +63,11 @@ target_link_libraries(
6263
velox_dwio_orc_reader
6364
velox_dwio_parquet_reader
6465
velox_dwio_parquet_writer
66+
velox_dynamic_library_loader
6567
velox_encode
6668
velox_exec
6769
velox_file
70+
velox_function_registry
6871
velox_functions_lib
6972
velox_functions_prestosql
7073
velox_gcs
@@ -100,6 +103,15 @@ set_property(TARGET presto_server_lib PROPERTY JOB_POOL_LINK
100103

101104
add_executable(presto_server PrestoMain.cpp)
102105

106+
# The below additional flags are necessary for resolving dependencies for
107+
# loading dynamic libraries.
108+
if(APPLE)
109+
target_link_options(presto_server BEFORE PUBLIC
110+
"-Wl,-undefined,dynamic_lookup")
111+
else()
112+
target_link_options(presto_server BEFORE PUBLIC "-Wl,-export-dynamic")
113+
endif()
114+
103115
# Moving velox_hive_connector and velox_tpch_connector to presto_server_lib
104116
# results in multiple link errors similar to the one below only on GCC.
105117
# "undefined reference to `vtable for velox::connector::tpch::TpchTableHandle`"

presto-native-execution/presto_cpp/main/PrestoServer.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "velox/common/base/StatsReporter.h"
4545
#include "velox/common/caching/CacheTTLController.h"
4646
#include "velox/common/caching/SsdCache.h"
47+
#include "velox/common/dynamic_registry/DynamicLibraryLoader.h"
4748
#include "velox/common/file/FileSystems.h"
4849
#include "velox/common/memory/MmapAllocator.h"
4950
#include "velox/common/memory/SharedArbitrator.h"
@@ -396,6 +397,7 @@ void PrestoServer::run() {
396397
registerRemoteFunctions();
397398
registerVectorSerdes();
398399
registerPrestoPlanNodeSerDe();
400+
registerDynamicFunctions();
399401

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

15991601
return nodeStatus;
16001602
}
1603+
void PrestoServer::registerDynamicFunctions() {
1604+
auto systemConfig = SystemConfig::instance();
1605+
if (!systemConfig->pluginDir().empty()) {
1606+
// If user provided path is valid, traverse and call dynamic function loader
1607+
// for all shared libraries.
1608+
const fs::path path(systemConfig->pluginDir());
1609+
PRESTO_STARTUP_LOG(INFO) << path;
1610+
std::error_code ec;
1611+
if (fs::is_directory(path, ec)) {
1612+
using recursiveDirectoryIterator =
1613+
std::filesystem::recursive_directory_iterator;
1614+
std::set<std::string> extensions{".so", ".dylib"};
1615+
for (const auto& dirEntry : recursiveDirectoryIterator(path)) {
1616+
// Skip any non shared library files and directories from loading.
1617+
auto dirEntryPath = dirEntry.path();
1618+
if (!fs::is_directory(dirEntry, ec) &&
1619+
extensions.find(dirEntryPath.extension()) != extensions.end()) {
1620+
velox::loadDynamicLibrary(dirEntryPath.c_str());
1621+
}
1622+
}
1623+
}
1624+
}
1625+
}
16011626

16021627
} // namespace facebook::presto

presto-native-execution/presto_cpp/main/PrestoServer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ class PrestoServer {
190190

191191
VeloxPlanValidator* getVeloxPlanValidator();
192192

193+
virtual void registerDynamicFunctions();
194+
193195
/// Invoked to get the list of filters passed to the http server.
194196
std::vector<std::unique_ptr<proxygen::RequestHandlerFactory>>
195197
getHttpServerFilters();

presto-native-execution/presto_cpp/main/common/Configs.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ SystemConfig::SystemConfig() {
240240
BOOL_PROP(kEnableRuntimeMetricsCollection, false),
241241
BOOL_PROP(kPlanValidatorFailOnNestedLoopJoin, false),
242242
STR_PROP(kPrestoDefaultNamespacePrefix, "presto.default"),
243+
STR_PROP(kPluginDir, ""),
243244
};
244245
}
245246

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

767+
std::string SystemConfig::pluginDir() const {
768+
return optionalProperty(kPluginDir).value();
769+
}
770+
766771
NodeConfig::NodeConfig() {
767772
registeredProps_ =
768773
std::unordered_map<std::string, folly::Optional<std::string>>{

presto-native-execution/presto_cpp/main/common/Configs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,8 @@ class SystemConfig : public ConfigBase {
642642
static constexpr std::string_view kInternalCommunicationJwtExpirationSeconds{
643643
"internal-communication.jwt.expiration-seconds"};
644644

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

899901
bool prestoNativeSidecar() const;
900902
std::string prestoDefaultNamespacePrefix() const;
903+
904+
std::string pluginDir() const;
901905
};
902906

903907
/// Provides access to node properties defined in node.properties file.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
add_subdirectory(examples)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
#pragma once
15+
16+
#include "presto_cpp/main/common/Configs.h"
17+
#include "velox/functions/Macros.h"
18+
#include "velox/functions/Registerer.h"
19+
namespace facebook::presto {
20+
template <template <class> class T, typename TReturn, typename... TArgs>
21+
void registerPrestoFunction(
22+
const char* name,
23+
const char* nameSpace = "",
24+
const std::vector<velox::exec::SignatureVariable>& constraints = {},
25+
bool overwrite = true) {
26+
std::string cpp_nameSpace(nameSpace);
27+
if (cpp_nameSpace.empty()) {
28+
auto systemConfig = SystemConfig::instance();
29+
cpp_nameSpace = systemConfig->prestoDefaultNamespacePrefix();
30+
}
31+
std::string cpp_name(cpp_nameSpace);
32+
cpp_name.append(name);
33+
LOG(INFO) << "registering function: " << cpp_name;
34+
facebook::velox::registerFunction<T, TReturn, TArgs...>(
35+
{cpp_name}, constraints, overwrite);
36+
}
37+
} // namespace facebook::presto
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Dynamic Loading of Presto CPP Extensions
2+
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.
3+
## Getting started
4+
1. Create a cpp file for your dynamic library
5+
6+
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.
7+
2. Register functions dynamically by creating .dylib or .so shared libraries and dropping them in a plugin directory
8+
9+
These shared libraries may be made using CMakeLists like the following:
10+
```
11+
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
12+
target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt Folly::folly gflags::gflags)
13+
```
14+
3. In the Prestissimo worker's config.properties file, set the plugin.dir property
15+
16+
Set the value of plugin.dir to the file path of the directory where the shared libraries are located.
17+
```
18+
plugin.dir="User\Test\Path\plugin"
19+
```
20+
When the worker or the sidecar process starts, it scans the plugin directory and attempts to dynamically load all shared libraries.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
add_library(velox_function_my_dynamic SHARED MyDynamicFunction.cpp)
14+
add_library(velox_varchar_function_my_dynamic SHARED
15+
MyDynamicVarcharFunction.cpp)
16+
add_library(velox_array_function_my_dynamic SHARED MyDynamicArrayFunction.cpp)
17+
18+
set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt gflags::gflags xsimd)
19+
target_link_libraries(velox_function_my_dynamic
20+
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
21+
target_link_libraries(velox_varchar_function_my_dynamic
22+
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
23+
target_link_libraries(velox_array_function_my_dynamic
24+
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"
17+
#include "velox/type/SimpleFunctionApi.h"
18+
19+
// This file defines a mock function that will be dynamically linked and
20+
// registered. There are no restrictions as to how the function needs to be
21+
// defined, but the library (.so) needs to provide a `void registry()` C
22+
// function in the top-level namespace.
23+
//
24+
// (note the extern "C" directive to prevent the compiler from mangling the
25+
// symbol name).
26+
27+
namespace facebook::velox::common::dynamicRegistry {
28+
29+
template <typename T>
30+
struct Dynamic123Function {
31+
VELOX_DEFINE_FUNCTION_TYPES(T);
32+
FOLLY_ALWAYS_INLINE bool call(
33+
int64_t& result,
34+
const arg_type<Array<int64_t>>& array) {
35+
result = array.size();
36+
return true;
37+
}
38+
};
39+
} // namespace facebook::velox::common::dynamicRegistry
40+
41+
extern "C" {
42+
void registry() {
43+
facebook::presto::registerPrestoFunction<
44+
facebook::velox::common::dynamicRegistry::Dynamic123Function,
45+
int64_t,
46+
facebook::velox::Array<int64_t>>({"dynamic_1"});
47+
}
48+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"
15+
16+
// This file defines a mock function that will be dynamically linked and
17+
// registered. There are no restrictions as to how the function needs to be
18+
// defined, but the library (.so) needs to provide a `void registry()` C
19+
// function in the top-level namespace.
20+
//
21+
// (note the extern "C" directive to prevent the compiler from mangling the
22+
// symbol name).
23+
24+
namespace facebook::velox::common::dynamicRegistry {
25+
26+
template <typename T>
27+
struct Dynamic123Function {
28+
FOLLY_ALWAYS_INLINE bool call(int64_t& result) {
29+
result = 123;
30+
return true;
31+
}
32+
};
33+
34+
} // namespace facebook::velox::common::dynamicRegistry
35+
36+
extern "C" {
37+
// In this case, we assume that facebook::velox::registerFunction
38+
// will be available and resolve when this library gets loaded.
39+
void registry() {
40+
facebook::presto::registerPrestoFunction<
41+
facebook::velox::common::dynamicRegistry::Dynamic123Function,
42+
int64_t>("dynamic_2");
43+
}
44+
}

0 commit comments

Comments
 (0)