Files
scylladb/cql3/statements/create_function_statement.cc
Wojciech Mitros 9281ba3919 wasm: reuse UDF instances
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>
2022-07-20 18:19:22 +02:00

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) {}
}
}