blob: a9f8a7e141661852b51d1781e3fe77a7c73c9f76 [file] [log] [blame]
/*
* Copyright 2015 The Kythe Authors. All rights reserved.
*
* Licensed 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.
*/
#include "KytheClaimClient.h"
#include <libmemcached/memcached.h>
#include <openssl/sha.h>
#include "absl/strings/str_format.h"
namespace kythe {
namespace {
constexpr char kArbitraryClaimantRoot[] = "KytheClaimClient";
} // anonymous namespace
bool KytheClaimClient::ClaimBatch(
std::vector<std::pair<std::string, bool>>* tokens) {
kythe::proto::VName claim;
claim.set_root(kArbitraryClaimantRoot);
bool success = false;
for (auto& token : *tokens) {
claim.set_signature(token.first);
if ((token.second = Claim(claim, claim))) {
success = true;
}
}
return success;
}
bool StaticClaimClient::Claim(const kythe::proto::VName& claimant,
const kythe::proto::VName& vname) {
const auto lookup = claim_table_.find(vname);
if (lookup == claim_table_.end()) {
// We don't know who's responsible for this VName.
return process_unknown_status_;
}
return VNameEquals(lookup->second, claimant);
}
void StaticClaimClient::AssignClaim(const kythe::proto::VName& claimable,
const kythe::proto::VName& claimant) {
claim_table_[claimable] = claimant;
}
DynamicClaimClient::~DynamicClaimClient() {
if (cache_) {
memcached_free(cache_);
cache_ = nullptr;
}
absl::FPrintF(
stderr, "%8lu %8lu claims approved/rejected (%f reject fraction)\n",
request_count_ - rejected_requests_, rejected_requests_,
request_count_ == 0 ? 0.0 : (double)rejected_requests_ / request_count_);
}
bool DynamicClaimClient::OpenMemcache(const std::string& spec) {
if (cache_) {
memcached_free(cache_);
cache_ = nullptr;
}
std::string spec_amend = spec;
spec_amend.append(" --BINARY-PROTOCOL");
cache_ = memcached(spec_amend.c_str(), spec_amend.size());
if (cache_ != nullptr) {
memcached_return_t remote_version = memcached_version(cache_);
return memcached_success(remote_version);
}
return false;
}
using Hash = unsigned char[SHA256_DIGEST_LENGTH];
void HashVName(const kythe::proto::VName& vname, size_t count, Hash* hash) {
::SHA256_CTX sha;
::SHA256_Init(&sha);
::SHA256_Update(&sha, vname.signature().c_str(), vname.signature().size());
::SHA256_Update(&sha, vname.path().c_str(), vname.path().size());
::SHA256_Update(&sha, vname.language().c_str(), vname.language().size());
::SHA256_Update(&sha, vname.root().c_str(), vname.root().size());
::SHA256_Update(&sha, vname.corpus().c_str(), vname.corpus().size());
::SHA256_Update(&sha, &count, sizeof(count));
::SHA256_Final(reinterpret_cast<unsigned char*>(hash), &sha);
}
bool DynamicClaimClient::Claim(const kythe::proto::VName& claimant,
const kythe::proto::VName& vname) {
// TODO(zarko): (It may not matter because this is already
// lossy, but) we only check to see whether anyone has made a claim on a
// vname at any point; if not, then we assume that we own that vname.
// If a claim exists in the cache (and isn't in the local claim_table_),
// regardless of whether it was ours, we assume that we do not own it.
++request_count_;
const auto lookup = claim_table_.find(vname);
if (lookup == claim_table_.end()) {
if (!cache_) {
// Fail open.
return true;
}
Hash claimant_hash, vname_hash;
HashVName(claimant, 0, &claimant_hash);
for (size_t tries = 0; tries < max_redundant_claims_; ++tries) {
HashVName(vname, tries, &vname_hash);
memcached_return_t add_result = memcached_add(
cache_, reinterpret_cast<const char*>(&vname_hash),
SHA256_DIGEST_LENGTH, reinterpret_cast<const char*>(&claimant_hash),
SHA256_DIGEST_LENGTH, 0, 0);
if (!memcached_success(add_result) &&
add_result != MEMCACHED_DATA_EXISTS) {
// We'll also pass the check below and assume we claimed the vname.
absl::FPrintF(stderr, "memcached add failed: %s\n",
memcached_strerror(cache_, add_result));
}
if (add_result != MEMCACHED_DATA_EXISTS) {
claim_table_[vname] = claimant;
return true;
}
}
// We failed all our tries, so assume we couldn't make a claim.
claim_table_[vname] = kythe::proto::VName();
++rejected_requests_;
return false;
}
if (VNameEquals(lookup->second, claimant)) {
return true;
} else {
++rejected_requests_;
return false;
}
}
void DynamicClaimClient::AssignClaim(const kythe::proto::VName& claimable,
const kythe::proto::VName& claimant) {
claim_table_[claimable] = claimant;
}
} // namespace kythe