int Ardb::BRPop(Context& ctx, RedisCommandFrame& cmd) { uint32 timeout; if (!string_touint32(cmd.GetArguments()[cmd.GetArguments().size() - 1], timeout)) { fill_error_reply(ctx.reply, "timeout is not an integer or out of range"); return 0; } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { if (ListPop(ctx, cmd.GetArguments()[i], false) == 0 && ctx.reply.type == REDIS_REPLY_STRING) { RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); fill_str_reply(r1, cmd.GetArguments()[i]); fill_str_reply(r2, ctx.reply.str); return 0; } } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { AddBlockKey(ctx, cmd.GetArguments()[i]); } if (NULL != ctx.client) { ctx.client->DetachFD(); if (timeout > 0) { ctx.block->blocking_timer_task_id = ctx.client->GetService().GetTimer().ScheduleHeapTask( new BlockConnectionTimeout(&ctx), timeout, -1, SECONDS); } } return 0; }
int Ardb::UnsubscribeChannel(Context& ctx, const std::string& channel, bool notify) { ctx.GetPubsub().pubsub_channels.erase(channel); WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock); PubsubContextTable::iterator it = m_pubsub_channels.find(channel); int ret = 0; if (it != m_pubsub_channels.end()) { it->second.erase(&ctx); if (it->second.empty()) { m_pubsub_channels.erase(it); } ret = 1; } if (notify && NULL != ctx.client) { RedisReply r; RedisReply& r1 = r.AddMember(); RedisReply& r2 = r.AddMember(); RedisReply& r3 = r.AddMember(); fill_str_reply(r1, "unsubscribe"); fill_str_reply(r2, channel); fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size()); ctx.client->Write(r); } return ret; }
int Comms::BLPop(Context& ctx, RedisCommandFrame& cmd) { uint32 timeout; if (!string_touint32(cmd.GetArguments()[cmd.GetArguments().size() - 1], timeout)) { fill_error_reply(ctx.reply, "timeout is not an integer or out of range"); return 0; } bool lpop = cmd.GetType() == REDIS_CMD_BLPOP; for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { std::string v; int err = lpop ? m_kv_store->LPop(ctx.currentDB, cmd.GetArguments()[i], v) : m_kv_store->RPop(ctx.currentDB, cmd.GetArguments()[i], v); if (0 == err && !v.empty()) { RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); fill_str_reply(r1, cmd.GetArguments()[i]); fill_str_reply(r2, v); FireKeyChangedEvent(ctx, ctx.currentDB, cmd.GetArguments()[i]); RedisCommandFrame list_pop(lpop ? "lpop" : "rpop"); list_pop.AddArg(cmd.GetArguments()[i]); RewriteClientCommand(ctx, list_pop); return 0; } if (err != 0 && err != mmkv::ERR_ENTRY_NOT_EXIST && err != mmkv::ERR_DB_NOT_EXIST) { FillErrorReply(ctx, err); return 0; } } if (NULL != ctx.client) { ctx.client->DetachFD(); ctx.GetBlockContext().lpop = cmd.GetType() == REDIS_CMD_BLPOP; if (timeout > 0) { ctx.block->blocking_timer_task_id = ctx.client->GetService().GetTimer().ScheduleHeapTask( new BlockListTimeout(&ctx), timeout, -1, SECONDS); } } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { AddBlockKey(ctx, cmd.GetArguments()[i]); } return 0; }
void Ardb::WakeBlockedConnCallback(Channel* ch, void* data) { if (NULL != ch) { Context* ctx = (Context*) data; if (-1 != ctx->block->blocking_timer_task_id) { ch->GetService().GetTimer().Cancel(ctx->block->blocking_timer_task_id); ctx->block->blocking_timer_task_id = -1; } if (ctx->block->dest_key.empty()) { ctx->reply.type = REDIS_REPLY_ARRAY; if (!ctx->block->waked_value.empty()) { RedisReply& r1 = ctx->reply.AddMember(); RedisReply& r2 = ctx->reply.AddMember(); fill_str_reply(r1, ctx->block->waked_key.key); fill_str_reply(r2, ctx->block->waked_value); RedisCommandFrame lpop("lpop"); lpop.AddArg(ctx->block->waked_key.key); g_db->m_master.FeedSlaves(ctx->currentDB, lpop); } } else { if (!ctx->block->waked_value.empty()) { fill_str_reply(ctx->reply, ctx->block->waked_value); RedisCommandFrame lpush("lpush"); lpush.AddArg(ctx->block->dest_key); lpush.AddArg(ctx->block->waked_value); g_db->LPush(*ctx, lpush); RedisCommandFrame lpop("lpop"); lpop.AddArg(ctx->block->waked_key.key); g_db->m_master.FeedSlaves(ctx->currentDB, lpop); g_db->m_master.FeedSlaves(ctx->currentDB, lpush); } else { ctx->reply.type = REDIS_REPLY_NIL; } } ch->Write(ctx->reply); g_db->ClearBlockKeys(*ctx); ch->AttachFD(); } }
int Ardb::HIncrbyFloat(Context& ctx, RedisCommandFrame& cmd) { double increment, val = 0; if (!GetDoubleValue(ctx, cmd.GetArguments()[2], increment)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data value; Data field(cmd.GetArguments()[1]); if (0 == err) { HashGet(ctx, meta, field, value); if (!value.GetDouble(val)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } } val += increment; std::string tmp; fast_dtoa(val, 10, tmp); value.SetString(tmp, false); HashSet(ctx, meta, field, value); fill_str_reply(ctx.reply, tmp); return 0; }
OP_NAMESPACE_BEGIN int Comms::LIndex(Context& ctx, RedisCommandFrame& cmd) { int64 index; if (!GetInt64Value(ctx, cmd.GetArguments()[1], index)) { return 0; } std::string v; int err = m_kv_store->LIndex(ctx.currentDB, cmd.GetArguments()[0], index, v); if (err >= 0) { fill_str_reply(ctx.reply, v); } else if (mmkv::ERR_OFFSET_OUTRANGE == err) { ctx.reply.type = REDIS_REPLY_NIL; } else { FillErrorReply(ctx, err); } return 0; }
int Ardb::UnsubscribeAll(Context& ctx, bool notify) { if(NULL == ctx.pubsub) { return 0; } StringSet tmp = ctx.GetPubsub().pubsub_channels; StringSet::iterator it = tmp.begin(); int count = 0; while (it != tmp.end()) { count += UnsubscribeChannel(ctx, *it, notify); it++; } if (notify && count == 0 && NULL != ctx.client) { RedisReply r; RedisReply& r1 = r.AddMember(); RedisReply& r2 = r.AddMember(); RedisReply& r3 = r.AddMember(); fill_str_reply(r1, "unsubscribe"); r2.type = REDIS_REPLY_NIL; fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size()); ctx.client->Write(r); } return 0; }
int Comms::RPopLPush(Context& ctx, RedisCommandFrame& cmd) { std::string v; int err = m_kv_store->RPopLPush(ctx.currentDB, cmd.GetArguments()[0], cmd.GetArguments()[1], v); if (err >= 0) { fill_str_reply(ctx.reply, v); FireKeyChangedEvent(ctx, cmd.GetArguments()[0]); FireKeyChangedEvent(ctx, cmd.GetArguments()[1]); if (cmd.GetType() == REDIS_CMD_BRPOPLPUSH) { ctx.current_cmd->SetCommand("rpoplpush"); } } else { if (err == mmkv::ERR_ENTRY_NOT_EXIST || err == mmkv::ERR_DB_NOT_EXIST) { ctx.reply.type = REDIS_REPLY_NIL; } else { FillErrorReply(ctx, err); } } return 0; }
void Comms::WakeBlockedList(Context& ctx, const std::string& key) { std::string v; int err = ctx.GetBlockContext().lpop ? m_kv_store->LPop(ctx.currentDB, key, v) : m_kv_store->RPop(ctx.currentDB, key, v); if (0 == err && !v.empty()) { if (ctx.GetBlockContext().push_key.empty()) { RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); fill_str_reply(r1, key); fill_str_reply(r2, v); if (!ctx.flags.no_wal) { RedisCommandFrame list_pop(ctx.GetBlockContext().lpop ? "lpop" : "rpop"); list_pop.AddArg(key); m_repl.WriteWAL(ctx.currentDB, list_pop); } } else { err = m_kv_store->RPush(ctx.currentDB, ctx.GetBlockContext().push_key, v); if (err < 0) { ctx.GetBlockContext().lpop ? m_kv_store->LPush(ctx.currentDB, key, v) : m_kv_store->RPush(ctx.currentDB, key, v); FillErrorReply(ctx, err); } else { fill_str_reply(ctx.reply, v); if (!ctx.flags.no_wal) { RedisCommandFrame rpoplpush("rpoplpush"); rpoplpush.AddArg(key); m_repl.WriteWAL(ctx.currentDB, rpoplpush); } } } ctx.client->Write(ctx.reply); ctx.client->AttachFD(); ClearBlockKeys(ctx); } }
int Ardb::PublishMessage(Context& ctx, const std::string& channel, const std::string& message) { ReadLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock); PubsubContextTable::iterator fit = m_pubsub_channels.find(channel); int receiver = 0; if (fit != m_pubsub_channels.end()) { ContextSet::iterator cit = fit->second.begin(); while (cit != fit->second.end()) { Context* cc = *cit; if (NULL != cc && cc->client != NULL) { RedisReply* r = NULL; NEW(r, RedisReply); RedisReply& r1 = r->AddMember(); RedisReply& r2 = r->AddMember(); RedisReply& r3 = r->AddMember(); fill_str_reply(r1, "message"); fill_str_reply(r2, channel); fill_str_reply(r3, message); cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r); receiver++; } cit++; } } PubsubContextTable::iterator pit = m_pubsub_patterns.begin(); while (pit != m_pubsub_patterns.end()) { const std::string& pattern = pit->first; if (stringmatchlen(pattern.c_str(), pattern.size(), channel.c_str(), channel.size(), 0)) { ContextSet::iterator cit = pit->second.begin(); while (cit != pit->second.end()) { Context* cc = *cit; if (NULL != cc && cc->client != NULL) { RedisReply* r = NULL; NEW(r, RedisReply); RedisReply& r1 = r->AddMember(); RedisReply& r2 = r->AddMember(); RedisReply& r3 = r->AddMember(); RedisReply& r4 = r->AddMember(); fill_str_reply(r1, "pmessage"); fill_str_reply(r2, pattern); fill_str_reply(r3, channel); fill_str_reply(r4, message); cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r); receiver++; } cit++; } } pit++; } return receiver; }
OP_NAMESPACE_BEGIN int Comms::Randomkey(Context& ctx, RedisCommandFrame& cmd) { std::string key; m_kv_store->RandomKey(ctx.currentDB, key); fill_str_reply(ctx.reply, key); return 0; }
int Ardb::SubscribeChannel(Context& ctx, const std::string& channel, bool notify) { ctx.GetPubsub().pubsub_channels.insert(channel); { WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock); m_pubsub_channels[channel].insert(&ctx); } if (notify && NULL != ctx.client) { RedisReply r; RedisReply& r1 = r.AddMember(); RedisReply& r2 = r.AddMember(); RedisReply& r3 = r.AddMember(); fill_str_reply(r1, "subscribe"); fill_str_reply(r2, channel); fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size()); ctx.client->Write(r); } return 0; }
void fill_str_array_reply(RedisReply& reply, T& v) { reply.type = REDIS_REPLY_ARRAY; typename T::iterator it = v.begin(); while (it != v.end()) { RedisReply r; fill_str_reply(r, *it); reply.elements.push_back(r); it++; } }
int ArdbServer::GetRange(ArdbConnContext& ctx, RedisCommandFrame& cmd) { int32 start, end; if (!string_toint32(cmd.GetArguments()[1], start) || !string_toint32(cmd.GetArguments()[2], end)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } std::string v; m_db->GetRange(ctx.currentDB, cmd.GetArguments()[0], start, end, v); fill_str_reply(ctx.reply, v); return 0; }
int ArdbServer::GetSet(ArdbConnContext& ctx, RedisCommandFrame& cmd) { std::string v; int ret = m_db->GetSet(ctx.currentDB, cmd.GetArguments()[0], cmd.GetArguments()[1], v); if (ret < 0) { ctx.reply.type = REDIS_REPLY_NIL; } else { fill_str_reply(ctx.reply, v); } return 0; }
int Ardb::RPopLPush(Context& ctx, RedisCommandFrame& cmd) { std::string v; if (ListPop(ctx, cmd.GetArguments()[0], false) == 0 && ctx.reply.type == REDIS_REPLY_STRING) { std::string value = ctx.reply.str; ListInsert(ctx, cmd.GetArguments()[1], NULL, ctx.reply.str, false, false); fill_str_reply(ctx.reply, value); } else { ctx.reply.type = REDIS_REPLY_NIL; } return 0; }
int ArdbServer::Get(ArdbConnContext& ctx, RedisCommandFrame& cmd) { const std::string& key = cmd.GetArguments()[0]; std::string value; if (0 == m_db->Get(ctx.currentDB, key, value)) { fill_str_reply(ctx.reply, value); //ctx.reply.type = REDIS_REPLY_NIL; } else { ctx.reply.type = REDIS_REPLY_NIL; } return 0; }
/* * CACHE LOAD|EVICT|STATUS key */ int Ardb::Cache(Context& ctx, RedisCommandFrame& cmd) { int ret = 0; if (!strcasecmp(cmd.GetArguments()[0].c_str(), "load")) { KeyType type = KEY_END; GetType(ctx, cmd.GetArguments()[1], type); if (type != KEY_END) { CacheLoadOptions options; options.deocode_geo = true; ret = m_cache.Load(ctx.currentDB, cmd.GetArguments()[1], type, options); } else { ret = ERR_INVALID_TYPE; } } else if (!strcasecmp(cmd.GetArguments()[0].c_str(), "evict")) { ret = m_cache.Evict(ctx.currentDB, cmd.GetArguments()[1]); } else if (!strcasecmp(cmd.GetArguments()[0].c_str(), "status")) { std::string status = m_cache.Status(ctx.currentDB, cmd.GetArguments()[1]); fill_str_reply(ctx.reply, status); return 0; } else { fill_error_reply(ctx.reply, "Syntax error, try CACHE (LOAD | EVICT | STATUS) key"); return 0; } if (ret == 0) { fill_status_reply(ctx.reply, "OK"); } else { fill_error_reply(ctx.reply, "Failed to cache load/evict/status key:%s", cmd.GetArguments()[1].c_str()); } return 0; }
int Comms::RPop(Context& ctx, RedisCommandFrame& cmd) { std::string v; int err = m_kv_store->RPop(ctx.currentDB, cmd.GetArguments()[0], v); if (err >= 0) { fill_str_reply(ctx.reply, v); FireKeyChangedEvent(ctx, cmd.GetArguments()[0]); } else { if (err == mmkv::ERR_ENTRY_NOT_EXIST || err == mmkv::ERR_DB_NOT_EXIST) { ctx.reply.type = REDIS_REPLY_NIL; } else { FillErrorReply(ctx, err); } } return 0; }
void fill_array_reply(RedisReply& reply, T& v) { reply.type = REDIS_REPLY_ARRAY; typename T::iterator it = v.begin(); while (it != v.end()) { ValueData& vo = *it; RedisReply r; if (vo.type == EMPTY_VALUE) { r.type = REDIS_REPLY_NIL; } else { std::string str; vo.ToBytes(); fill_str_reply(r, vo.bytes_value); } reply.elements.push_back(r); it++; } }
int Ardb::HKeys(Context& ctx, RedisCommandFrame& cmd) { ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; if (0 != err) { return 0; } HashIterator iter; err = HashIter(ctx, meta, "", iter, true); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; while (iter.Valid()) { const Data* value = iter.Field(); RedisReply& r = ctx.reply.AddMember(); std::string tmp; fill_str_reply(r, value->GetDecodeString(tmp)); iter.Next(); } return 0; }
int Ardb::ListRange(Context& ctx, const Slice& key, int64 start, int64 end) { ValueObject meta; int err = GetMetaValue(ctx, key, LIST_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); /* convert negative indexes */ if (start < 0) start = meta.meta.Length() + start; if (end < 0) end = meta.meta.Length() + end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ ctx.reply.type = REDIS_REPLY_ARRAY; if (start > end || start >= meta.meta.Length()) { return 0; } if (end >= meta.meta.Length()) end = meta.meta.Length() - 1; int64 rangelen = (end - start) + 1; if (meta.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { uint32 i = start; while (rangelen--) { RedisReply& r = ctx.reply.AddMember(); fill_value_reply(r, meta.meta.ziplist[i++]); } } else { ListIterator iter; if (meta.meta.IsSequentialList()) { SequencialListIter(ctx, meta, iter, start); uint32 count = 0; while (iter.Valid() && count < rangelen) { RedisReply& r = ctx.reply.AddMember(); fill_str_reply(r, iter.Element()->ToString()); count++; iter.Next(); } } else { if (start > meta.meta.len / 2) { start -= meta.meta.len; end -= meta.meta.len; } ListIter(ctx, meta, iter, start < 0); int64 cursor = start < 0 ? -1 : 0; while (iter.Valid()) { if (cursor >= start && cursor <= end) { RedisReply& r = ctx.reply.AddMember(); fill_str_reply(r, iter.Element()->ToString()); } if (start < 0) { if (cursor < start) { break; } cursor--; } else { if (cursor > end) { break; } cursor++; } iter.Next(); } } } return 0; }
int Ardb::Scan(Context& ctx, RedisCommandFrame& cmd) { StringArray keys; std::string pattern; uint32 limit = 10000; //return max 10000 keys one time if (cmd.GetArguments().size() > 1) { for (uint32 i = 1; i < cmd.GetArguments().size(); i++) { if (!strcasecmp(cmd.GetArguments()[i].c_str(), "count")) { if (i + 1 >= cmd.GetArguments().size() || !string_touint32(cmd.GetArguments()[i + 1], limit)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } i++; } else if (!strcasecmp(cmd.GetArguments()[i].c_str(), "match")) { if (i + 1 >= cmd.GetArguments().size()) { fill_error_reply(ctx.reply, "'MATCH' need one args followed"); return 0; } pattern = cmd.GetArguments()[i + 1]; i++; } else { fill_error_reply(ctx.reply, "Syntax error, try scan 0 "); return 0; } } } RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); r2.type = REDIS_REPLY_ARRAY; KeyObject from; from.db = ctx.currentDB; from.type = KEY_META; const std::string& cursor = cmd.GetArguments()[0]; std::string scan_start_cursor; if (cmd.GetArguments()[0] != "0") { if (m_cfg.scan_redis_compatible) { FindElementByRedisCursor(cursor, scan_start_cursor); } from.key = scan_start_cursor; } Iterator* iter = IteratorKeyValue(from, false); bool reachend = false; std::string tmpkey; while (NULL != iter && iter->Valid()) { KeyObject kk; if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META) { reachend = true; break; } tmpkey.clear(); tmpkey.assign(kk.key.data(), kk.key.size()); if (r2.MemberSize() >= limit) { break; } if ((pattern.empty() || stringmatchlen(pattern.c_str(), pattern.size(), tmpkey.c_str(), tmpkey.size(), 0) == 1)) { RedisReply& rr1 = r2.AddMember(); fill_str_reply(rr1, tmpkey); } iter->Next(); } if (reachend || !iter->Valid()) { fill_str_reply(r1, "0"); } else { if (m_cfg.scan_redis_compatible) { uint64 newcursor = GetNewRedisCursor(tmpkey); fill_str_reply(r1, stringfromll(newcursor)); } else { fill_str_reply(r1, tmpkey); } } DELETE(iter); return 0; }
int Ardb::KeysOperation(Context& ctx, const KeysOptions& options) { KeyObject from; from.db = ctx.currentDB; from.type = KEY_META; std::string::size_type cursor = options.pattern.find("*"); if (cursor == std::string::npos) { from.key = options.pattern; } else { if (options.pattern != "*" && cursor > 0) { from.key = options.pattern.substr(0, cursor); } } Iterator* iter = IteratorKeyValue(from, false); std::string tmpkey; uint32 count = 0; while (NULL != iter && iter->Valid()) { KeyObject kk; if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META) { break; } tmpkey.clear(); tmpkey.assign(kk.key.data(), kk.key.size()); if ((options.pattern == "*" || stringmatchlen(options.pattern.c_str(), options.pattern.size(), tmpkey.data(), tmpkey.size(), 0) == 1)) { if (options.op == OP_GET) { RedisReply& r = ctx.reply.AddMember(); fill_str_reply(r, tmpkey); } else { count++; } } else { /* * If the '*' is the last char, we can break iteration now */ if (cursor == options.pattern.size() - 1) { break; } } iter->Next(); } DELETE(iter); if (options.op == OP_GET) { ctx.reply.type = REDIS_REPLY_ARRAY; } else { fill_int_reply(ctx.reply, count); } return 0; }
int Ardb::Randomkey(Context& ctx, RedisCommandFrame& cmd) { ctx.reply.type = REDIS_REPLY_NIL; KeyObject from; from.db = ctx.currentDB; from.type = KEY_META; std::string min_key, max_key, randkey; Iterator* iter = IteratorKeyValue(from, false); if (iter->Valid()) { KeyObject kk; if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META) { goto _end; } min_key.assign(kk.key.data(), kk.key.size()); max_key = min_key; from.type = SET_ELEMENT; //Refer comparator.cpp from.encode_buf.Clear(); IteratorSeek(iter, from); if (!iter->Valid()) { iter->SeekToLast(); } if (iter->Valid()) { iter->Prev(); if (iter->Valid()) { if (decode_key(iter->Key(), kk) && kk.db == ctx.currentDB && kk.type == KEY_META) { max_key.assign(kk.key.data(), kk.key.size()); } } } randkey = min_key; if (min_key < max_key) { randkey = random_between_string(min_key, max_key); from.type = KEY_META; from.db = ctx.currentDB; from.key = randkey; IteratorSeek(iter, from); if (iter->Valid()) { KeyObject kk; if (!decode_key(iter->Key(), kk) || kk.db != ctx.currentDB || kk.type != KEY_META) { goto _end; } randkey.assign(kk.key.data(), kk.key.size()); } } } if (!randkey.empty()) { fill_str_reply(ctx.reply, randkey); } _end: DELETE(iter); return 0; }
int ArdbServer::Echo(ArdbConnContext& ctx, RedisCommandFrame& cmd) { fill_str_reply(ctx.reply, cmd.GetArguments()[0]); return 0; }
int Ardb::HScan(Context& ctx, RedisCommandFrame& cmd) { std::string pattern; uint32 limit = 10000; //return max 10000 keys one time if (cmd.GetArguments().size() > 2) { for (uint32 i = 2; i < cmd.GetArguments().size(); i++) { if (!strcasecmp(cmd.GetArguments()[i].c_str(), "count")) { if (i + 1 >= cmd.GetArguments().size() || !string_touint32(cmd.GetArguments()[i + 1], limit)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } i++; } else if (!strcasecmp(cmd.GetArguments()[i].c_str(), "match")) { if (i + 1 >= cmd.GetArguments().size()) { fill_error_reply(ctx.reply, "'MATCH' need one args followed"); return 0; } pattern = cmd.GetArguments()[i + 1]; i++; } else { fill_error_reply(ctx.reply, "Syntax error, try scan 0 "); return 0; } } } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); r2.type = REDIS_REPLY_ARRAY; if (0 != err) { fill_str_reply(r1, "0"); return 0; } const std::string& cursor = cmd.GetArguments()[1]; HashIterator iter; HashIter(ctx, meta, cursor == "0" ? "" : cursor, iter, true); while (iter.Valid()) { const Data* field = iter.Field(); std::string tmp; field->GetDecodeString(tmp); if ((pattern.empty() || fnmatch(pattern.c_str(), tmp.c_str(), 0) == 0)) { RedisReply& rr1 = r2.AddMember(); RedisReply& rr2 = r2.AddMember(); fill_str_reply(rr1, tmp); fill_value_reply(rr2, *(iter.Value())); } if (r2.MemberSize() >= (limit * 2)) { break; } iter.Next(); } if (iter.Valid()) { iter.Next(); const Data* next_field = iter.Field(); std::string tmp; fill_str_reply(r1, next_field->GetDecodeString(tmp)); } else { fill_str_reply(r1, "0"); } return 0; }
int Ardb::GeoSearchByOptions(Context& ctx, ValueObject& meta, GeoSearchOptions& options) { uint64 start_time = get_current_epoch_micros(); int ret = 0; double x = options.x, y = options.y; if (options.by_member) { Data element, score; element.SetString(options.member, true); ret = ZSetScore(ctx, meta, element, score); if (0 != ret || score.IsNil()) { return -1; } GeoHashHelper::GetMercatorXYByHash(score.value.iv, x, y); //GeoHashHelper::GetXYByHash(score.value.iv, x, y); } else { if (options.coord_type != GEO_MERCATOR_TYPE) { x = GeoHashHelper::GetMercatorX(options.x); y = GeoHashHelper::GetMercatorY(options.y); } } DEBUG_LOG("####Step1: Cost %lluus", get_current_epoch_micros() - start_time); GeoPointArray points; ZSetRangeByScoreOptions fetch_options; fetch_options.withscores = false; fetch_options.op = OP_GET; fetch_options.fill_reply = false; fetch_options.fetch_geo_location = true; if (options.in_members) { StringSet::iterator it = options.submembers.begin(); while (it != options.submembers.end()) { Data element, score; element.SetString(*it, true); Location loc; ret = ZSetScore(ctx, meta, element, score, &loc); if (0 == ret) { fetch_options.results.push_back(element); fetch_options.locs.push_back(loc); } it++; } } else { GeoHashBitsSet ress; GeoHashHelper::GetAreasByRadiusV2(GEO_MERCATOR_TYPE, y, x, options.radius, ress); /* * Merge areas if possible to avoid disk search */ std::vector<ZRangeSpec> range_array; GeoHashBitsSet::iterator rit = ress.begin(); typedef TreeMap<uint64, uint64>::Type HashRangeMap; HashRangeMap tmp; while (rit != ress.end()) { GeoHashBits& hash = *rit; GeoHashBits next = hash; next.bits++; tmp[GeoHashHelper::Allign60Bits(hash)] = GeoHashHelper::Allign60Bits(next); rit++; } HashRangeMap::iterator tit = tmp.begin(); HashRangeMap::iterator nit = tmp.begin(); nit++; while (tit != tmp.end()) { ZRangeSpec range; range.contain_min = true; range.contain_max = true; range.min.SetInt64(tit->first); range.max.SetInt64(tit->second); while (nit != tmp.end() && nit->first == range.max.value.iv) { range.max.SetInt64(nit->second); nit++; tit++; } range_array.push_back(range); nit++; tit++; } DEBUG_LOG("After areas merging, reduce searching area size from %u to %u", ress.size(), range_array.size()); std::vector<ZRangeSpec>::iterator hit = range_array.begin(); ZSetIterator* iter = NULL; while (hit != range_array.end()) { ZRangeSpec& range = *hit; uint64 t1 = get_current_epoch_millis(); ZSetRangeByScore(ctx, meta, range, fetch_options, iter); uint64 t2 = get_current_epoch_millis(); DEBUG_LOG("####Cost %llums to range fetch", t2 - t1); hit++; } DELETE(iter); } DEBUG_LOG("####Step2: Cost %lluus", get_current_epoch_micros() - start_time); uint32 outrange = 0; LocationDeque::iterator lit = fetch_options.locs.begin(); DataArray::iterator vit = fetch_options.results.begin(); while (vit != fetch_options.results.end()) { Location& loc = *lit; GeoPoint point; point.x = loc.x; point.y = loc.y; /* * distance accuracy is 0.2m */ if (GeoHashHelper::GetDistanceSquareIfInRadius(GEO_MERCATOR_TYPE, x, y, point.x, point.y, options.radius, point.distance, 0.2)) { vit->GetDecodeString(point.value); /* * filter by exclude/include */ if (!options.includes.empty() || !options.excludes.empty()) { Data subst; subst.SetString(point.value, false); bool matched = options.includes.empty() ? true : false; if (!options.includes.empty()) { StringStringMap::const_iterator sit = options.includes.begin(); while (sit != options.includes.end()) { Data mv; if (0 != MatchValueByPattern(ctx, sit->first, sit->second, subst, mv)) { matched = false; break; } else { matched = true; } sit++; } } if (matched && !options.excludes.empty()) { StringStringMap::const_iterator sit = options.excludes.begin(); while (sit != options.excludes.end()) { Data mv; if (0 == MatchValueByPattern(ctx, sit->first, sit->second, subst, mv)) { matched = false; break; } else { matched = true; } sit++; } } if (matched) { points.push_back(point); } } else { points.push_back(point); } } else { outrange++; } vit++; lit++; } DEBUG_LOG("###Result size:%d, outrange:%d", points.size(), outrange); DEBUG_LOG("####Step3: Cost %lluus", get_current_epoch_micros() - start_time); if (!options.nosort) { std::sort(points.begin(), points.end(), options.asc ? less_by_distance : great_by_distance); } DEBUG_LOG("####Step3.5: Cost %lluus", get_current_epoch_micros() - start_time); if (options.offset > 0) { if ((uint32) options.offset > points.size()) { points.clear(); } else { GeoPointArray::iterator start = points.begin() + options.offset; points.erase(points.begin(), start); } } if (options.limit > 0) { if ((uint32) options.limit < points.size()) { GeoPointArray::iterator end = points.begin() + options.limit; points.erase(end, points.end()); } } DEBUG_LOG("####Step4: Cost %lluus", get_current_epoch_micros() - start_time); ValueObjectMap meta_cache; GeoPointArray::iterator pit = points.begin(); while (pit != points.end()) { RedisReply& r = ctx.reply.AddMember(); fill_str_reply(r, pit->value); GeoGetOptionArray::const_iterator ait = options.get_patterns.begin(); while (ait != options.get_patterns.end()) { if (ait->get_distances) { RedisReply& rr = ctx.reply.AddMember(); rr.type = REDIS_REPLY_STRING; char dbuf[128]; int dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", sqrt(pit->distance)); rr.str.assign(dbuf, dlen); } else if (ait->get_coodinates) { if (options.coord_type == GEO_WGS84_TYPE) { pit->x = GeoHashHelper::GetWGS84X(pit->x); pit->y = GeoHashHelper::GetWGS84Y(pit->y); } RedisReply& rr1 = ctx.reply.AddMember(); RedisReply& rr2 = ctx.reply.AddMember(); if (options.coord_type == GEO_WGS84_TYPE) { fill_double_reply(rr1, pit->x); fill_double_reply(rr2, pit->y); } else { char dbuf[128]; int dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", pit->x); rr1.type = REDIS_REPLY_STRING; rr1.str.assign(dbuf, dlen); dlen = snprintf(dbuf, sizeof(dbuf), "%.2f", pit->y); rr2.type = REDIS_REPLY_STRING; rr2.str.assign(dbuf, dlen); } } else if (ait->hgetall) { std::string keystr(ait->get_pattern.data(), ait->get_pattern.size()); string_replace(keystr, "*", pit->value); RedisReply& rr = ctx.reply.AddMember(); rr.type = REDIS_REPLY_ARRAY; HashGetAll(ctx, keystr, rr); } else { Data v, attr; v.SetString(pit->value, false); GetValueByPattern(ctx, ait->get_pattern, v, attr, &meta_cache); RedisReply& rr = ctx.reply.AddMember(); fill_value_reply(rr, attr); } ait++; } pit++; } DEBUG_LOG("####Step5: Cost %lluus", get_current_epoch_micros() - start_time); uint64 end_time = get_current_epoch_micros(); DEBUG_LOG("Cost %llu microseconds to search.", end_time - start_time); return points.size(); }