When executing a wasm UDF, most of the time is spent on setting up the instance. To minimize its cost, we reuse the instance using wasm::instance_cache. This patch adds a wasm instance cache, that stores a wasmtime instance for each UDF and scheduling group. The instances are evicted using LRU strategy. The cache may store some entries for the UDF after evicting the instance, but they are evicted when the corresponding UDF is dropped, which greatly limits their number. The size of stored instances is estimated using the size of their WASM memories. In order to be able to read the size of memory, we require that the memory is exported by the client. Signed-off-by: Wojciech Mitros <wojciech.mitros@scylladb.com>
89 lines
3.8 KiB
C++
89 lines
3.8 KiB
C++
/*
|
|
* Copyright (C) 2019-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#include <seastar/core/coroutine.hh>
|
|
#include "cql3/statements/create_function_statement.hh"
|
|
#include "cql3/functions/functions.hh"
|
|
#include "cql3/functions/user_function.hh"
|
|
#include "prepared_statement.hh"
|
|
#include "service/migration_manager.hh"
|
|
#include "service/storage_proxy.hh"
|
|
#include "lang/lua.hh"
|
|
#include "data_dictionary/data_dictionary.hh"
|
|
#include "replica/database.hh" // for wasm
|
|
#include "cql3/query_processor.hh"
|
|
|
|
namespace cql3 {
|
|
|
|
namespace statements {
|
|
|
|
shared_ptr<functions::function> create_function_statement::create(query_processor& qp, functions::function* old) const {
|
|
if (old && !dynamic_cast<functions::user_function*>(old)) {
|
|
throw exceptions::invalid_request_exception(format("Cannot replace '{}' which is not a user defined function", *old));
|
|
}
|
|
if (_language != "lua" && _language != "xwasm") {
|
|
throw exceptions::invalid_request_exception(format("Language '{}' is not supported", _language));
|
|
}
|
|
data_type return_type = prepare_type(qp, *_return_type);
|
|
std::vector<sstring> arg_names;
|
|
for (const auto& arg_name : _arg_names) {
|
|
arg_names.push_back(arg_name->to_string());
|
|
}
|
|
|
|
auto&& db = qp.db();
|
|
if (_language == "lua") {
|
|
auto cfg = lua::make_runtime_config(db.get_config());
|
|
functions::user_function::context ctx = functions::user_function::lua_context {
|
|
.bitcode = lua::compile(cfg, arg_names, _body),
|
|
.cfg = cfg,
|
|
};
|
|
|
|
return ::make_shared<functions::user_function>(_name, _arg_types, std::move(arg_names), _body, _language,
|
|
std::move(return_type), _called_on_null_input, std::move(ctx));
|
|
} else if (_language == "xwasm") {
|
|
// FIXME: need better way to test wasm compilation without real_database()
|
|
wasm::context ctx{db.real_database().wasm_engine(), _name.name, qp.get_wasm_instance_cache()};
|
|
try {
|
|
wasm::compile(ctx, arg_names, _body);
|
|
return ::make_shared<functions::user_function>(_name, _arg_types, std::move(arg_names), _body, _language,
|
|
std::move(return_type), _called_on_null_input, std::move(ctx));
|
|
} catch (const wasm::exception& we) {
|
|
throw exceptions::invalid_request_exception(we.what());
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<prepared_statement> create_function_statement::prepare(data_dictionary::database db, cql_stats& stats) {
|
|
return std::make_unique<prepared_statement>(make_shared<create_function_statement>(*this));
|
|
}
|
|
|
|
future<std::pair<::shared_ptr<cql_transport::event::schema_change>, std::vector<mutation>>>
|
|
create_function_statement::prepare_schema_mutations(query_processor& qp, api::timestamp_type ts) const {
|
|
::shared_ptr<cql_transport::event::schema_change> ret;
|
|
std::vector<mutation> m;
|
|
|
|
auto func = dynamic_pointer_cast<functions::user_function>(validate_while_executing(qp));
|
|
|
|
if (func) {
|
|
m = co_await qp.get_migration_manager().prepare_new_function_announcement(func, ts);
|
|
ret = create_schema_change(*func, true);
|
|
}
|
|
|
|
co_return std::make_pair(std::move(ret), std::move(m));
|
|
}
|
|
|
|
create_function_statement::create_function_statement(functions::function_name name, sstring language, sstring body,
|
|
std::vector<shared_ptr<column_identifier>> arg_names, std::vector<shared_ptr<cql3_type::raw>> arg_types,
|
|
shared_ptr<cql3_type::raw> return_type, bool called_on_null_input, bool or_replace, bool if_not_exists)
|
|
: create_function_statement_base(std::move(name), std::move(arg_types), or_replace, if_not_exists),
|
|
_language(std::move(language)), _body(std::move(body)), _arg_names(std::move(arg_names)),
|
|
_return_type(std::move(return_type)), _called_on_null_input(called_on_null_input) {}
|
|
}
|
|
}
|