blob: d21c7201c49425a8af0f27c4476d614efafa1e4a [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <queue>
#include <deque>
#include <list>
#include "proxy_layer.h"
#include "geo/lib/geo_client.h"
namespace dsn {
namespace apps {
class rrdb_client;
}
}
class proxy_test;
namespace pegasus {
namespace proxy {
// http://redis.io/topics/protocol
class redis_parser : public proxy_session
{
protected:
friend class ::proxy_test;
struct redis_base_type
{
virtual ~redis_base_type() = default;
virtual void marshalling(::dsn::binary_writer &write_stream) const = 0;
};
struct redis_integer : public redis_base_type
{
int64_t value = 0;
explicit redis_integer(int64_t v = 0) : value(v) {}
void marshalling(::dsn::binary_writer &write_stream) const final;
};
// represent both redis simple string and error
struct redis_simple_string : public redis_base_type
{
bool is_error = false;
std::string message;
redis_simple_string(bool err, std::string &&msg) : is_error(err), message(std::move(msg)) {}
void marshalling(::dsn::binary_writer &write_stream) const final;
};
struct redis_bulk_string : public redis_base_type
{
int length = -1; // max length is 512 MB
::dsn::blob data;
redis_bulk_string() = default;
redis_bulk_string(std::string str)
{
data = ::dsn::blob::create_from_bytes(std::move(str));
length = data.length();
}
explicit redis_bulk_string(const ::dsn::blob &bb) : length(bb.length()), data(bb) {}
void marshalling(::dsn::binary_writer &write_stream) const final;
};
struct redis_array : public redis_base_type
{
int count = -1;
std::vector<std::shared_ptr<redis_base_type>> array;
void resize(size_t size)
{
count = size;
array.resize(size);
}
void marshalling(::dsn::binary_writer &write_stream) const final;
};
struct redis_request
{
int sub_request_count = 0;
std::vector<redis_bulk_string> sub_requests;
redis_request(int count = 0, std::vector<redis_bulk_string> requests = {})
: sub_request_count(count), sub_requests(std::move(requests))
{
}
};
struct message_entry
{
redis_request request;
std::atomic<dsn::message_ex *> response;
int64_t sequence_id = 0;
};
bool parse(dsn::message_ex *msg) override;
// this is virtual only because we can override and test other modules
virtual void handle_command(std::unique_ptr<message_entry> &&entry);
private:
// queue for pipeline the response
dsn::zlock response_lock;
std::deque<std::unique_ptr<message_entry>> pending_response;
enum parser_status
{
kStartArray,
kInArraySize,
kStartBulkString,
kInBulkStringSize,
kStartBulkStringData,
};
// recieving message and parsing status
// [
// content for current parser
redis_bulk_string _current_str;
std::unique_ptr<message_entry> _current_msg;
parser_status _status;
std::string _current_size;
// data stream content
std::queue<dsn::message_ex *> _recv_buffers;
size_t _total_length;
char *_current_buffer;
size_t _current_buffer_length;
size_t _current_cursor;
// ]
// for rrdb
std::unique_ptr<::dsn::apps::rrdb_client> client;
std::unique_ptr<geo::geo_client> _geo_client;
protected:
// function for data stream
void append_message(dsn::message_ex *msg);
void prepare_current_buffer();
char peek();
bool eat(char c);
void eat_all(char *dest, size_t length);
void reset_parser();
// function for parser
bool end_array_size();
bool end_bulk_string_size();
void append_current_bulk_string();
bool parse_stream();
// function for rrdb operation
#define DECLARE_REDIS_HANDLER(function_name) \
void function_name(message_entry &req); \
static void g_##function_name(redis_parser *_this, message_entry &req) \
{ \
_this->function_name(req); \
}
DECLARE_REDIS_HANDLER(set)
DECLARE_REDIS_HANDLER(get)
DECLARE_REDIS_HANDLER(del)
DECLARE_REDIS_HANDLER(setex)
DECLARE_REDIS_HANDLER(ttl)
DECLARE_REDIS_HANDLER(geo_add)
DECLARE_REDIS_HANDLER(geo_dist)
DECLARE_REDIS_HANDLER(geo_pos)
DECLARE_REDIS_HANDLER(geo_radius)
DECLARE_REDIS_HANDLER(geo_radius_by_member)
DECLARE_REDIS_HANDLER(incr)
DECLARE_REDIS_HANDLER(incr_by)
DECLARE_REDIS_HANDLER(decr)
DECLARE_REDIS_HANDLER(decr_by)
DECLARE_REDIS_HANDLER(default_handler)
void set_internal(message_entry &entry);
void set_geo_internal(message_entry &entry);
void del_internal(message_entry &entry);
void del_geo_internal(message_entry &entry);
void counter_internal(message_entry &entry);
static void parse_set_parameters(const std::vector<redis_bulk_string> &opts, int &ttl_seconds);
static void parse_geo_radius_parameters(const std::vector<redis_bulk_string> &opts,
int base_index,
double &radius_m,
std::string &unit,
geo::geo_client::SortType &sort_type,
int &count,
bool &WITHCOORD,
bool &WITHDIST,
bool &WITHHASH);
void process_geo_radius_result(message_entry &entry,
const std::string &unit,
bool WITHCOORD,
bool WITHDIST,
bool WITHHASH,
int ec,
std::list<geo::SearchResult> &&results);
// function for pipeline reply
void enqueue_pending_response(std::unique_ptr<message_entry> &&entry);
void fetch_and_dequeue_messages(std::vector<dsn::message_ex *> &msgs, bool only_ready_ones);
void clear_reply_queue();
void reply_all_ready();
template <typename T>
void reply_message(message_entry &entry, const T &value)
{
dsn::message_ex *resp = create_response();
// release in reply_all_ready or reset
resp->add_ref();
dsn::rpc_write_stream s(resp);
value.marshalling(s);
s.commit_buffer();
entry.response.store(resp, std::memory_order_release);
reply_all_ready();
}
std::shared_ptr<redis_bulk_string> construct_bulk_string(double data);
void simple_ok_reply(message_entry &entry);
void simple_error_reply(message_entry &entry, const std::string &message);
void simple_string_reply(message_entry &entry, bool is_error, std::string message);
void simple_integer_reply(message_entry &entry, int64_t value);
typedef void (*redis_call_handler)(redis_parser *, message_entry &);
static std::unordered_map<std::string, redis_call_handler> s_dispatcher;
static redis_call_handler get_handler(const char *command, unsigned int length);
static std::atomic_llong s_next_seqid;
static const char CR;
static const char LF;
public:
redis_parser(proxy_stub *op, dsn::message_ex *first_msg);
~redis_parser() override;
};
}
} // namespace