diff --git a/FDBLibTLS/FDBLibTLS.vcxproj b/FDBLibTLS/FDBLibTLS.vcxproj index 3b369f11dd4..be6ed76f6f2 100644 --- a/FDBLibTLS/FDBLibTLS.vcxproj +++ b/FDBLibTLS/FDBLibTLS.vcxproj @@ -15,6 +15,8 @@ + + diff --git a/FDBLibTLS/FDBLibTLSPolicy.cpp b/FDBLibTLS/FDBLibTLSPolicy.cpp index 992c6b34169..4229b64c55b 100644 --- a/FDBLibTLS/FDBLibTLSPolicy.cpp +++ b/FDBLibTLS/FDBLibTLSPolicy.cpp @@ -23,8 +23,7 @@ #include #include -#include -#include +#include #include #include @@ -34,10 +33,12 @@ #include #include +#include +#include FDBLibTLSPolicy::FDBLibTLSPolicy(Reference plugin, ITLSLogFunc logf): - plugin(plugin), logf(logf), tls_cfg(NULL), session_created(false), cert_data_set(false), - key_data_set(false), verify_peers_set(false), verify_cert(true), verify_time(true) { + plugin(plugin), logf(logf), tls_cfg(NULL), roots(NULL), session_created(false), ca_data_set(false), + cert_data_set(false), key_data_set(false), verify_peers_set(false) { if ((tls_cfg = tls_config_new()) == NULL) { logf("FDBLibTLSConfigError", NULL, true, NULL); @@ -46,251 +47,73 @@ FDBLibTLSPolicy::FDBLibTLSPolicy(Reference plugin, ITLSLogFunc // Require client certificates for authentication. tls_config_verify_client(tls_cfg); - - // Name verification is always manually handled (if requested via configuration). - tls_config_insecure_noverifyname(tls_cfg); } FDBLibTLSPolicy::~FDBLibTLSPolicy() { + sk_X509_pop_free(roots, X509_free); tls_config_free(tls_cfg); } -ITLSSession* FDBLibTLSPolicy::create_session(bool is_client, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid) { - session_created = true; - try { - return new FDBLibTLSSession(Reference::addRef(this), is_client, send_func, send_ctx, recv_func, recv_ctx, uid); - } catch ( ... ) { - return NULL; - } -} - -static int hexValue(char c) { - static char const digits[] = "0123456789ABCDEF"; - - if (c >= 'a' && c <= 'f') - c -= ('a' - 'A'); - - int value = std::find(digits, digits + 16, c) - digits; - if (value >= 16) { - throw std::runtime_error("hexValue"); - } - return value; -} - -// Does not handle "raw" form (e.g. #28C4D1), only escaped text -static std::string de4514(std::string const& input, int start, int& out_end) { - std::string output; - - if(input[start] == '#' || input[start] == ' ') { - out_end = start; - return output; - } - - int space_count = 0; - - for(int p = start; p < input.size();) { - switch(input[p]) { - case '\\': // Handle escaped sequence - - // Backslash escaping nothing! - if(p == input.size() - 1) { - out_end = p; - goto FIN; - } - - switch(input[p+1]) { - case ' ': - case '"': - case '#': - case '+': - case ',': - case ';': - case '<': - case '=': - case '>': - case '\\': - output += input[p+1]; - p += 2; - space_count = 0; - continue; - - default: - // Backslash escaping pair of hex digits requires two characters - if(p == input.size() - 2) { - out_end = p; - goto FIN; - } - - try { - output += hexValue(input[p+1]) * 16 + hexValue(input[p+2]); - p += 3; - space_count = 0; - continue; - } catch( ... ) { - out_end = p; - goto FIN; - } - } +ITLSSession* FDBLibTLSPolicy::create_session(bool is_client, const char* servername, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid) { + if (is_client) { + // If verify peers has been set then there is no point specifying a + // servername, since this will be ignored - the servername should be + // matched by the verify criteria instead. + if (verify_peers_set && servername != NULL) { + logf("FDBLibTLSVerifyPeersWithServerName", NULL, true, NULL); + return NULL; + } - case '"': - case '+': - case ',': - case ';': - case '<': - case '>': - case 0: - // All of these must have been escaped - out_end = p; - goto FIN; - - default: - // Character is what it is - output += input[p]; - if(input[p] == ' ') - space_count++; - else - space_count = 0; - p++; + // If verify peers has not been set, then require a server name to + // avoid an accidental lack of name validation. + if (!verify_peers_set && servername == NULL) { + logf("FDBLibTLSNoServerName", NULL, true, NULL); + return NULL; } } - out_end = input.size(); - - FIN: - out_end -= space_count; - output.resize(output.size() - space_count); - - return output; -} - -static std::pair splitPair(std::string const& input, char c) { - int p = input.find_first_of(c); - if(p == input.npos) { - throw std::runtime_error("splitPair"); + session_created = true; + try { + return new FDBLibTLSSession(Reference::addRef(this), is_client, servername, send_func, send_ctx, recv_func, recv_ctx, uid); + } catch ( ... ) { + return NULL; } - return std::make_pair(input.substr(0, p), input.substr(p+1, input.size())); -} - -static int abbrevToNID(std::string const& sn) { - int nid = NID_undef; - - if (sn == "C" || sn == "CN" || sn == "L" || sn == "ST" || sn == "O" || sn == "OU") - nid = OBJ_sn2nid(sn.c_str()); - if (nid == NID_undef) - throw std::runtime_error("abbrevToNID"); - - return nid; } -void FDBLibTLSPolicy::parse_verify(std::string input) { - int s = 0; - - while (s < input.size()) { - int eq = input.find('=', s); - - if (eq == input.npos) - throw std::runtime_error("parse_verify"); +static int password_cb(char *buf, int size, int rwflag, void *u) { + const char *password = (const char *)u; + int plen; - std::string term = input.substr(s, eq - s); + if (size < 0) + return 0; + if (u == NULL) + return 0; - if (term.find("Check.") == 0) { - if (eq + 2 > input.size()) - throw std::runtime_error("parse_verify"); - if (eq + 2 != input.size() && input[eq + 2] != ',') - throw std::runtime_error("parse_verify"); + plen = strlen(password); + if (plen > size) + return 0; - bool* flag; + // Note: buf does not need to be NUL-terminated since + // we return an explicit length. + strncpy(buf, password, size); - if (term == "Check.Valid") - flag = &verify_cert; - else if (term == "Check.Unexpired") - flag = &verify_time; - else - throw std::runtime_error("parse_verify"); - - if (input[eq + 1] == '0') - *flag = false; - else if (input[eq + 1] == '1') - *flag = true; - else - throw std::runtime_error("parse_verify"); - - s = eq + 3; - } else { - std::map* criteria = &subject_criteria; - - if (term.find('.') != term.npos) { - auto scoped = splitPair(term, '.'); - - if (scoped.first == "S" || scoped.first == "Subject") - criteria = &subject_criteria; - else if (scoped.first == "I" || scoped.first == "Issuer") - criteria = &issuer_criteria; - else - throw std::runtime_error("parse_verify"); - - term = scoped.second; - } - - int remain; - auto unesc = de4514(input, eq + 1, remain); - - if (remain == eq + 1) - throw std::runtime_error("parse_verify"); - - criteria->insert(std::make_pair(abbrevToNID(term), unesc)); - - if (remain != input.size() && input[remain] != ',') - throw std::runtime_error("parse_verify"); - - s = remain + 1; - } - } -} - -void FDBLibTLSPolicy::reset_verify() { - verify_cert = true; - verify_time = true; - subject_criteria = {}; - issuer_criteria = {}; + return plen; } -int password_cb(char *buf, int size, int rwflag, void *u) { - // A no-op password callback is provided simply to stop libcrypto - // from trying to use its own password reading functionality. - return 0; -} - -bool FDBLibTLSPolicy::set_cert_data(const uint8_t* cert_data, int cert_len) { +struct stack_st_X509* FDBLibTLSPolicy::parse_cert_pem(const uint8_t* cert_pem, size_t cert_pem_len) { struct stack_st_X509 *certs = NULL; - unsigned long errnum; X509 *cert = NULL; BIO *bio = NULL; - long data_len; - char *data; + int errnum; bool rc = false; - // The cert data contains one or more PEM encoded certificates - the - // first certificate is for this host, with any additional certificates - // being the full certificate chain. As such, the last certificate - // is the trusted root certificate. If only one certificate is provided - // then it is required to be a self-signed certificate, which is also - // treated as the trusted root. - - if (cert_data_set) { - logf("FDBLibTLSCertAlreadySet", NULL, true, NULL); - goto err; - } - if (session_created) { - logf("FDBLibTLSPolicyAlreadyActive", NULL, true, NULL); + if (cert_pem_len > INT_MAX) goto err; - } - - if ((certs = sk_X509_new_null()) == NULL) { + if ((bio = BIO_new_mem_buf((void *)cert_pem, cert_pem_len)) == NULL) { logf("FDBLibTLSOutOfMemory", NULL, true, NULL); goto err; } - if ((bio = BIO_new_mem_buf((void *)cert_data, cert_len)) == NULL) { + if ((certs = sk_X509_new_null()) == NULL) { logf("FDBLibTLSOutOfMemory", NULL, true, NULL); goto err; } @@ -318,79 +141,134 @@ bool FDBLibTLSPolicy::set_cert_data(const uint8_t* cert_data, int cert_len) { goto err; } - BIO_free_all(bio); - if ((bio = BIO_new(BIO_s_mem())) == NULL) { - logf("FDBLibTLSOutOfMemory", NULL, true, NULL); - goto err; - } - if (!PEM_write_bio_X509(bio, sk_X509_value(certs, sk_X509_num(certs) - 1))) { - logf("FDBLibTLSCertWriteError", NULL, true, NULL); - goto err; + BIO_free(bio); + + return certs; + + err: + sk_X509_pop_free(certs, X509_free); + X509_free(cert); + BIO_free(bio); + + return NULL; +} + +bool FDBLibTLSPolicy::set_ca_data(const uint8_t* ca_data, int ca_len) { + if (ca_data_set) { + logf("FDBLibTLSCAAlreadySet", NULL, true, NULL); + return false; } - if ((data_len = BIO_get_mem_data(bio, &data)) <= 0) { - logf("FDBLibTLSCertError", NULL, true, NULL); - goto err; + if (session_created) { + logf("FDBLibTLSPolicyAlreadyActive", NULL, true, NULL); + return false; } - if (tls_config_set_ca_mem(tls_cfg, (const uint8_t *)data, data_len) == -1) { - logf("FDBLibTLSSetCAError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); - goto err; - } + if (ca_len < 0) + return false; + sk_X509_pop_free(roots, X509_free); + if ((roots = parse_cert_pem(ca_data, ca_len)) == NULL) + return false; - if (sk_X509_num(certs) > 1) { - BIO_free_all(bio); - if ((bio = BIO_new(BIO_s_mem())) == NULL) { - logf("FDBLibTLSOutOfMemory", NULL, true, NULL); - goto err; - } - for (int i = 0; i < sk_X509_num(certs) - 1; i++) { - if (!PEM_write_bio_X509(bio, sk_X509_value(certs, i))) { - logf("FDBLibTLSCertWriteError", NULL, true, NULL); - goto err; - } - } - if ((data_len = BIO_get_mem_data(bio, &data)) <= 0) { - logf("FDBLibTLSCertError", NULL, true, NULL); - goto err; - } + if (tls_config_set_ca_mem(tls_cfg, ca_data, ca_len) == -1) { + logf("FDBLibTLSCAError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); + return false; } - if (tls_config_set_cert_mem(tls_cfg, (const uint8_t *)data, data_len) == -1) { - logf("FDBLibTLSSetCertError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); - goto err; + ca_data_set = true; + + return true; +} + +bool FDBLibTLSPolicy::set_cert_data(const uint8_t* cert_data, int cert_len) { + if (cert_data_set) { + logf("FDBLibTLSCertAlreadySet", NULL, true, NULL); + return false; + } + if (session_created) { + logf("FDBLibTLSPolicyAlreadyActive", NULL, true, NULL); + return false; } - rc = true; + if (tls_config_set_cert_mem(tls_cfg, cert_data, cert_len) == -1) { + logf("FDBLibTLSCertError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); + return false; + } - err: - sk_X509_pop_free(certs, X509_free); - X509_free(cert); - BIO_free_all(bio); + cert_data_set = true; - return rc; + return true; } -bool FDBLibTLSPolicy::set_key_data(const uint8_t* key_data, int key_len) { +bool FDBLibTLSPolicy::set_key_data(const uint8_t* key_data, int key_len, const char* password) { + EVP_PKEY *key = NULL; + BIO *bio = NULL; + bool rc = false; + if (key_data_set) { logf("FDBLibTLSKeyAlreadySet", NULL, true, NULL); - return false; + goto err; } if (session_created) { logf("FDBLibTLSPolicyAlreadyActive", NULL, true, NULL); - return false; + goto err; } - if (tls_config_set_key_mem(tls_cfg, key_data, key_len) == -1) { - logf("FDBLibTLSKeyError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); - return false; + if (password != NULL) { + char *data; + long len; + + if ((bio = BIO_new_mem_buf((void *)key_data, key_len)) == NULL) { + logf("FDBLibTLSOutOfMemory", NULL, true, NULL); + goto err; + } + ERR_clear_error(); + if ((key = PEM_read_bio_PrivateKey(bio, NULL, password_cb, (void *)password)) == NULL) { + int errnum = ERR_peek_error(); + char errbuf[256]; + + if ((ERR_GET_LIB(errnum) == ERR_LIB_PEM && ERR_GET_REASON(errnum) == PEM_R_BAD_DECRYPT) || + (ERR_GET_LIB(errnum) == ERR_LIB_EVP && ERR_GET_REASON(errnum) == EVP_R_BAD_DECRYPT)) { + logf("FDBLibTLSIncorrectPassword", NULL, true, NULL); + } else { + ERR_error_string_n(errnum, errbuf, sizeof(errbuf)); + logf("FDBLibTLSPrivateKeyError", NULL, true, "LibcryptoErrorMessage", errbuf, NULL); + } + goto err; + } + BIO_free(bio); + if ((bio = BIO_new(BIO_s_mem())) == NULL) { + logf("FDBLibTLSOutOfMemory", NULL, true, NULL); + goto err; + } + if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) { + logf("FDBLibTLSOutOfMemory", NULL, true, NULL); + goto err; + } + if ((len = BIO_get_mem_data(bio, &data)) <= 0) { + logf("FDBLibTLSOutOfMemory", NULL, true, NULL); + goto err; + } + if (tls_config_set_key_mem(tls_cfg, (const uint8_t *)data, len) == -1) { + logf("FDBLibTLSKeyError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); + goto err; + } + } else { + if (tls_config_set_key_mem(tls_cfg, key_data, key_len) == -1) { + logf("FDBLibTLSKeyError", NULL, true, "LibTLSErrorMessage", tls_config_error(tls_cfg), NULL); + goto err; + } } key_data_set = true; + rc = true; - return true; + err: + BIO_free(bio); + EVP_PKEY_free(key); + return rc; } -bool FDBLibTLSPolicy::set_verify_peers(const uint8_t* verify_peers, int verify_peers_len) { +bool FDBLibTLSPolicy::set_verify_peers(int count, const uint8_t* verify_peers[], int verify_peers_len[]) { if (verify_peers_set) { logf("FDBLibTLSVerifyPeersAlreadySet", NULL, true, NULL); return false; @@ -400,19 +278,39 @@ bool FDBLibTLSPolicy::set_verify_peers(const uint8_t* verify_peers, int verify_p return false; } - try { - parse_verify(std::string((const char*)verify_peers, verify_peers_len)); - } catch ( const std::runtime_error& e ) { - reset_verify(); - logf("FDBLibTLSVerifyPeersParseError", NULL, true, "Config", verify_peers, NULL); + if (count < 1) { + logf("FDBLibTLSNoVerifyPeers", NULL, true, NULL); return false; } - if (!verify_cert) - tls_config_insecure_noverifycert(tls_cfg); + for (int i = 0; i < count; i++) { + try { + std::string verifyString((const char*)verify_peers[i], verify_peers_len[i]); + int start = 0; + while(start < verifyString.size()) { + int split = verifyString.find('|', start); + if(split == std::string::npos) { + break; + } + if(split == start || verifyString[split-1] != '\\') { + Reference verify = Reference(new FDBLibTLSVerify(verifyString.substr(start,split-start))); + verify_rules.push_back(verify); + start = split+1; + } + } + Reference verify = Reference(new FDBLibTLSVerify(verifyString.substr(start))); + verify_rules.push_back(verify); + } catch ( const std::runtime_error& e ) { + verify_rules.clear(); + logf("FDBLibTLSVerifyPeersParseError", NULL, true, "Config", verify_peers[i], NULL); + return false; + } + } - if (!verify_time) - tls_config_insecure_noverifytime(tls_cfg); + // All verification is manually handled (as requested via configuration). + tls_config_insecure_noverifycert(tls_cfg); + tls_config_insecure_noverifyname(tls_cfg); + tls_config_insecure_noverifytime(tls_cfg); verify_peers_set = true; diff --git a/FDBLibTLS/FDBLibTLSPolicy.h b/FDBLibTLS/FDBLibTLSPolicy.h index 0fb87c9ae88..94717a1aaa0 100644 --- a/FDBLibTLS/FDBLibTLSPolicy.h +++ b/FDBLibTLS/FDBLibTLSPolicy.h @@ -23,12 +23,14 @@ #pragma once -#include "FDBLibTLSPlugin.h" #include "ITLSPlugin.h" #include "ReferenceCounted.h" -#include +#include "FDBLibTLSPlugin.h" +#include "FDBLibTLSVerify.h" + #include +#include struct FDBLibTLSPolicy: ITLSPolicy, ReferenceCounted { FDBLibTLSPolicy(Reference plugin, ITLSLogFunc logf); @@ -40,28 +42,29 @@ struct FDBLibTLSPolicy: ITLSPolicy, ReferenceCounted { Reference plugin; ITLSLogFunc logf; - virtual ITLSSession* create_session(bool is_client, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid); + virtual ITLSSession* create_session(bool is_client, const char* servername, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid); + struct stack_st_X509* parse_cert_pem(const uint8_t* cert_pem, size_t cert_pem_len); void parse_verify(std::string input); void reset_verify(void); + virtual bool set_ca_data(const uint8_t* ca_data, int ca_len); virtual bool set_cert_data(const uint8_t* cert_data, int cert_len); - virtual bool set_key_data(const uint8_t* key_data, int key_len); - virtual bool set_verify_peers(const uint8_t* verify_peers, int verify_peers_len); + virtual bool set_key_data(const uint8_t* key_data, int key_len, const char* password); + virtual bool set_verify_peers(int count, const uint8_t* verify_peers[], int verify_peers_len[]); - struct tls_config *tls_cfg; + struct tls_config* tls_cfg; bool session_created; + bool ca_data_set; bool cert_data_set; bool key_data_set; bool verify_peers_set; - bool verify_cert; - bool verify_time; + struct stack_st_X509* roots; - std::map subject_criteria; - std::map issuer_criteria; + std::vector> verify_rules; }; #endif /* FDB_LIBTLS_POLICY_H */ diff --git a/FDBLibTLS/FDBLibTLSSession.cpp b/FDBLibTLS/FDBLibTLSSession.cpp index a2071e2abb3..5b20615dd64 100644 --- a/FDBLibTLS/FDBLibTLSSession.cpp +++ b/FDBLibTLS/FDBLibTLSSession.cpp @@ -21,12 +21,14 @@ #include "FDBLibTLSSession.h" #include +#include #include #include +#include #include -#include +#include #include #include @@ -54,8 +56,9 @@ static ssize_t tls_write_func(struct tls *ctx, const void *buf, size_t buflen, v return (ssize_t)rv; } -FDBLibTLSSession::FDBLibTLSSession(Reference policy, bool is_client, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid) : - tls_ctx(NULL), tls_sctx(NULL), policy(policy), send_func(send_func), send_ctx(send_ctx), recv_func(recv_func), recv_ctx(recv_ctx), handshake_completed(false), uid(uid) { +FDBLibTLSSession::FDBLibTLSSession(Reference policy, bool is_client, const char* servername, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid) : + tls_ctx(NULL), tls_sctx(NULL), is_client(is_client), policy(policy), send_func(send_func), send_ctx(send_ctx), + recv_func(recv_func), recv_ctx(recv_ctx), handshake_completed(false), uid(uid) { if (is_client) { if ((tls_ctx = tls_client()) == NULL) { @@ -67,7 +70,7 @@ FDBLibTLSSession::FDBLibTLSSession(Reference policy, bool is_cl tls_free(tls_ctx); throw std::runtime_error("FDBLibTLSConfigureError"); } - if (tls_connect_cbs(tls_ctx, tls_read_func, tls_write_func, this, NULL) == -1) { + if (tls_connect_cbs(tls_ctx, tls_read_func, tls_write_func, this, servername) == -1) { policy->logf("FDBLibTLSConnectError", uid, true, "LibTLSErrorMessage", tls_error(tls_ctx), NULL); tls_free(tls_ctx); throw std::runtime_error("FDBLibTLSConnectError"); @@ -97,8 +100,6 @@ FDBLibTLSSession::~FDBLibTLSSession() { tls_free(tls_sctx); } -int password_cb(char *buf, int size, int rwflag, void *u); - bool match_criteria(X509_NAME *name, int nid, const char *value, size_t len) { unsigned char *name_entry_utf8 = NULL, *criteria_utf8 = NULL; int name_entry_utf8_len, criteria_utf8_len; @@ -138,65 +139,125 @@ bool match_criteria(X509_NAME *name, int nid, const char *value, size_t len) { return rc; } -bool FDBLibTLSSession::check_criteria() { +std::tuple FDBLibTLSSession::check_verify(Reference verify, struct stack_st_X509 *certs) { + X509_STORE_CTX *store_ctx = NULL; X509_NAME *subject, *issuer; - const uint8_t *cert_pem; - size_t cert_pem_len; - X509 *cert = NULL; BIO *bio = NULL; bool rc = false; + // if returning false, give a reason string + std::string reason = ""; // If certificate verification is disabled, there's nothing more to do. - if (!policy->verify_cert) - return true; - - // If no criteria have been specified, then we're done. - if (policy->subject_criteria.size() == 0 && policy->issuer_criteria.size() == 0) - return true; + if (!verify->verify_cert) + return std::make_tuple(true, reason); - if ((cert_pem = tls_peer_cert_chain_pem(tls_ctx, &cert_pem_len)) == NULL) { - policy->logf("FDBLibTLSNoCertError", uid, true, NULL); + // Verify the certificate. + if ((store_ctx = X509_STORE_CTX_new()) == NULL) { + policy->logf("FDBLibTLSOutOfMemory", uid, true, NULL); + reason = "FDBLibTLSOutOfMemory"; goto err; } - if ((bio = BIO_new_mem_buf((void *)cert_pem, cert_pem_len)) == NULL) { - policy->logf("FDBLibTLSOutOfMemory", NULL, true, NULL); + if (!X509_STORE_CTX_init(store_ctx, NULL, sk_X509_value(certs, 0), certs)) { + reason = "FDBLibTLSStoreCtxInit"; goto err; } - if ((cert = PEM_read_bio_X509(bio, NULL, password_cb, NULL)) == NULL) { - policy->logf("FDBLibTLSCertPEMError", uid, true, NULL); + X509_STORE_CTX_trusted_stack(store_ctx, policy->roots); + X509_STORE_CTX_set_default(store_ctx, is_client ? "ssl_server" : "ssl_client"); + if (!verify->verify_time) + X509_VERIFY_PARAM_set_flags(X509_STORE_CTX_get0_param(store_ctx), X509_V_FLAG_NO_CHECK_TIME); + if (X509_verify_cert(store_ctx) <= 0) { + const char *errstr = X509_verify_cert_error_string(X509_STORE_CTX_get_error(store_ctx)); + reason = "FDBLibTLSVerifyCert VerifyError " + std::string(errstr); goto err; } // Check subject criteria. - if ((subject = X509_get_subject_name(cert)) == NULL) { - policy->logf("FDBLibTLSCertSubjectError", uid, true, NULL); + if ((subject = X509_get_subject_name(sk_X509_value(store_ctx->chain, 0))) == NULL) { + reason = "FDBLibTLSCertSubjectError"; goto err; } - for (auto &pair: policy->subject_criteria) { + for (auto &pair: verify->subject_criteria) { if (!match_criteria(subject, pair.first, pair.second.c_str(), pair.second.size())) { - policy->logf("FDBLibTLSCertSubjectMatchFailure", uid, true, NULL); + reason = "FDBLibTLSCertSubjectMatchFailure"; goto err; } - } + } // Check issuer criteria. - if ((issuer = X509_get_issuer_name(cert)) == NULL) { - policy->logf("FDBLibTLSCertIssuerError", uid, true, NULL); + if ((issuer = X509_get_issuer_name(sk_X509_value(store_ctx->chain, 0))) == NULL) { + reason = "FDBLibTLSCertIssuerError"; goto err; } - for (auto &pair: policy->issuer_criteria) { + for (auto &pair: verify->issuer_criteria) { if (!match_criteria(issuer, pair.first, pair.second.c_str(), pair.second.size())) { - policy->logf("FDBLibTLSCertIssuerMatchFailure", uid, true, NULL); + reason = "FDBLibTLSCertIssuerMatchFailure"; + goto err; + } + } + + // Check root criteria - this is the subject of the final certificate in the stack. + if ((subject = X509_get_subject_name(sk_X509_value(store_ctx->chain, sk_X509_num(store_ctx->chain) - 1))) == NULL) { + reason = "FDBLibTLSRootSubjectError"; + goto err; + } + for (auto &pair: verify->root_criteria) { + if (!match_criteria(subject, pair.first, pair.second.c_str(), pair.second.size())) { + reason = "FDBLibTLSRootSubjectMatchFailure"; goto err; } - } + } // If we got this far, everything checked out... rc = true; err: - BIO_free_all(bio); - X509_free(cert); + X509_STORE_CTX_free(store_ctx); + + return std::make_tuple(rc, reason); +} + +bool FDBLibTLSSession::verify_peer() { + struct stack_st_X509 *certs = NULL; + const uint8_t *cert_pem; + size_t cert_pem_len; + bool rc = false; + std::set verify_failure_reasons; + bool verify_success; + std::string verify_failure_reason; + + // If no verify peer rules have been set, we are relying on standard + // libtls verification. + if (policy->verify_rules.empty()) + return true; + + if ((cert_pem = tls_peer_cert_chain_pem(tls_ctx, &cert_pem_len)) == NULL) { + policy->logf("FDBLibTLSNoCertError", uid, true, NULL); + goto err; + } + if ((certs = policy->parse_cert_pem(cert_pem, cert_pem_len)) == NULL) + goto err; + + // Any matching rule is sufficient. + for (auto &verify_rule: policy->verify_rules) { + std::tie(verify_success, verify_failure_reason) = check_verify(verify_rule, certs); + if (verify_success) { + rc = true; + break; + } else { + if (verify_failure_reason.length() > 0) + verify_failure_reasons.insert(verify_failure_reason); + } + } + + if (!rc) { + // log the various failure reasons + for (std::string reason : verify_failure_reasons) { + policy->logf(reason.c_str(), uid, false, NULL); + } + } + + err: + sk_X509_pop_free(certs, X509_free); return rc; } @@ -206,7 +267,7 @@ int FDBLibTLSSession::handshake() { switch (rv) { case 0: - if (!check_criteria()) + if (!verify_peer()) return FAILED; handshake_completed = true; return SUCCESS; diff --git a/FDBLibTLS/FDBLibTLSSession.h b/FDBLibTLS/FDBLibTLSSession.h index d150cc97327..035ced05dc1 100644 --- a/FDBLibTLS/FDBLibTLSSession.h +++ b/FDBLibTLS/FDBLibTLSSession.h @@ -27,17 +27,19 @@ #include "ReferenceCounted.h" #include "FDBLibTLSPolicy.h" +#include "FDBLibTLSVerify.h" #include struct FDBLibTLSSession : ITLSSession, ReferenceCounted { - FDBLibTLSSession(Reference policy, bool is_client, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid); + FDBLibTLSSession(Reference policy, bool is_client, const char* servername, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid); virtual ~FDBLibTLSSession(); virtual void addref() { ReferenceCounted::addref(); } virtual void delref() { ReferenceCounted::delref(); } - bool check_criteria(); + bool verify_peer(); + std::tuple check_verify(Reference verify, struct stack_st_X509 *certs); virtual int handshake(); virtual int read(uint8_t* data, int length); @@ -45,6 +47,8 @@ struct FDBLibTLSSession : ITLSSession, ReferenceCounted { Reference policy; + bool is_client; + struct tls *tls_ctx; struct tls *tls_sctx; diff --git a/FDBLibTLS/FDBLibTLSVerify.cpp b/FDBLibTLS/FDBLibTLSVerify.cpp new file mode 100644 index 00000000000..0c28a2f036b --- /dev/null +++ b/FDBLibTLS/FDBLibTLSVerify.cpp @@ -0,0 +1,221 @@ +/* + * FDBLibTLSVerify.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors + * + * 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 "FDBLibTLSVerify.h" + +#include + +#include +#include + +static int hexValue(char c) { + static char const digits[] = "0123456789ABCDEF"; + + if (c >= 'a' && c <= 'f') + c -= ('a' - 'A'); + + int value = std::find(digits, digits + 16, c) - digits; + if (value >= 16) { + throw std::runtime_error("hexValue"); + } + return value; +} + +// Does not handle "raw" form (e.g. #28C4D1), only escaped text +static std::string de4514(std::string const& input, int start, int& out_end) { + std::string output; + + if(input[start] == '#' || input[start] == ' ') { + out_end = start; + return output; + } + + int space_count = 0; + + for(int p = start; p < input.size();) { + switch(input[p]) { + case '\\': // Handle escaped sequence + + // Backslash escaping nothing! + if(p == input.size() - 1) { + out_end = p; + goto FIN; + } + + switch(input[p+1]) { + case ' ': + case '"': + case '#': + case '+': + case ',': + case ';': + case '<': + case '=': + case '>': + case '|': + case '\\': + output += input[p+1]; + p += 2; + space_count = 0; + continue; + + default: + // Backslash escaping pair of hex digits requires two characters + if(p == input.size() - 2) { + out_end = p; + goto FIN; + } + + try { + output += hexValue(input[p+1]) * 16 + hexValue(input[p+2]); + p += 3; + space_count = 0; + continue; + } catch( ... ) { + out_end = p; + goto FIN; + } + } + + case '"': + case '+': + case ',': + case ';': + case '<': + case '>': + case 0: + // All of these must have been escaped + out_end = p; + goto FIN; + + default: + // Character is what it is + output += input[p]; + if(input[p] == ' ') + space_count++; + else + space_count = 0; + p++; + } + } + + out_end = input.size(); + + FIN: + out_end -= space_count; + output.resize(output.size() - space_count); + + return output; +} + +static std::pair splitPair(std::string const& input, char c) { + int p = input.find_first_of(c); + if(p == input.npos) { + throw std::runtime_error("splitPair"); + } + return std::make_pair(input.substr(0, p), input.substr(p+1, input.size())); +} + +static int abbrevToNID(std::string const& sn) { + int nid = NID_undef; + + if (sn == "C" || sn == "CN" || sn == "L" || sn == "ST" || sn == "O" || sn == "OU" || sn == "UID" || sn == "DC") + nid = OBJ_sn2nid(sn.c_str()); + if (nid == NID_undef) + throw std::runtime_error("abbrevToNID"); + + return nid; +} + +FDBLibTLSVerify::FDBLibTLSVerify(std::string verify_config): + verify_cert(true), verify_time(true) { + parse_verify(verify_config); +} + +FDBLibTLSVerify::~FDBLibTLSVerify() { +} + +void FDBLibTLSVerify::parse_verify(std::string input) { + int s = 0; + + while (s < input.size()) { + int eq = input.find('=', s); + + if (eq == input.npos) + throw std::runtime_error("parse_verify"); + + std::string term = input.substr(s, eq - s); + + if (term.find("Check.") == 0) { + if (eq + 2 > input.size()) + throw std::runtime_error("parse_verify"); + if (eq + 2 != input.size() && input[eq + 2] != ',') + throw std::runtime_error("parse_verify"); + + bool* flag; + + if (term == "Check.Valid") + flag = &verify_cert; + else if (term == "Check.Unexpired") + flag = &verify_time; + else + throw std::runtime_error("parse_verify"); + + if (input[eq + 1] == '0') + *flag = false; + else if (input[eq + 1] == '1') + *flag = true; + else + throw std::runtime_error("parse_verify"); + + s = eq + 3; + } else { + std::map* criteria = &subject_criteria; + + if (term.find('.') != term.npos) { + auto scoped = splitPair(term, '.'); + + if (scoped.first == "S" || scoped.first == "Subject") + criteria = &subject_criteria; + else if (scoped.first == "I" || scoped.first == "Issuer") + criteria = &issuer_criteria; + else if (scoped.first == "R" || scoped.first == "Root") + criteria = &root_criteria; + else + throw std::runtime_error("parse_verify"); + + term = scoped.second; + } + + int remain; + auto unesc = de4514(input, eq + 1, remain); + + if (remain == eq + 1) + throw std::runtime_error("parse_verify"); + + criteria->insert(std::make_pair(abbrevToNID(term), unesc)); + + if (remain != input.size() && input[remain] != ',') + throw std::runtime_error("parse_verify"); + + s = remain + 1; + } + } +} diff --git a/FDBLibTLS/FDBLibTLSVerify.h b/FDBLibTLS/FDBLibTLSVerify.h new file mode 100644 index 00000000000..e00a8b995c1 --- /dev/null +++ b/FDBLibTLS/FDBLibTLSVerify.h @@ -0,0 +1,50 @@ +/* + * FDBLibTLSVerify.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors + * + * 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. + */ + +#ifndef FDB_LIBTLS_VERIFY_H +#define FDB_LIBTLS_VERIFY_H + +#pragma once + +#include + +#include "ReferenceCounted.h" + +#include +#include + +struct FDBLibTLSVerify: ReferenceCounted { + FDBLibTLSVerify(std::string verify); + virtual ~FDBLibTLSVerify(); + + virtual void addref() { ReferenceCounted::addref(); } + virtual void delref() { ReferenceCounted::delref(); } + + void parse_verify(std::string input); + + bool verify_cert; + bool verify_time; + + std::map subject_criteria; + std::map issuer_criteria; + std::map root_criteria; +}; + +#endif /* FDB_LIBTLS_VERIFY_H */ diff --git a/FDBLibTLS/ITLSPlugin.h b/FDBLibTLS/ITLSPlugin.h deleted file mode 100644 index 40779d2e720..00000000000 --- a/FDBLibTLS/ITLSPlugin.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ITLSPlugin.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * 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. - */ - -#ifndef FDB_ITLSPLUGIN_H -#define FDB_ITLSPLUGIN_H - -#pragma once - -#include - -struct ITLSSession { - enum { SUCCESS = 0, WANT_READ = -1, WANT_WRITE = -2, FAILED = -3 }; - - virtual void addref() = 0; - virtual void delref() = 0; - - // handshake should return SUCCESS if the handshake is complete, - // FAILED on fatal error, or one of WANT_READ or WANT_WRITE if the - // handshake should be reattempted after more data can be - // read/written on the underlying connection. - virtual int handshake() = 0; - - // read should return the (non-zero) number of bytes read, - // WANT_READ or WANT_WRITE if the operation is blocked by the - // underlying stream, or FAILED if there is an error (including a - // closed connection). - virtual int read(uint8_t* data, int length) = 0; - - // write should return the (non-zero) number of bytes written, or - // WANT_READ or WANT_WRITE if the operation is blocked by the - // underlying stream, or FAILED if there is an error. - virtual int write(const uint8_t* data, int length) = 0; -}; - -// Returns the number of bytes sent (possibly 0), or -1 on error -// (including connection close) -typedef int (*TLSSendCallbackFunc)(void* ctx, const uint8_t* buf, int len); - -// Returns the number of bytes read (possibly 0), or -1 on error -// (including connection close) -typedef int (*TLSRecvCallbackFunc)(void* ctx, uint8_t* buf, int len); - -struct ITLSPolicy { - virtual void addref() = 0; - virtual void delref() = 0; - - // set_cert_data should import the provided certificate list and - // associate it with this policy. cert_data will point to a PEM - // encoded certificate list, ordered such that each certificate - // certifies the one before it. - // - // cert_data may additionally contain key information, which must - // be ignored. - // - // set_cert_data should return true if the operation succeeded, - // and false otherwise. After the first call to create_session for - // a given policy, set_cert_data should immediately return false - // if called. - virtual bool set_cert_data(const uint8_t* cert_data, int cert_len) = 0; - - // set_key_data should import the provided private key and - // associate it with this policy. key_data will point to a PEM - // encoded key. - // - // key_data may additionally contain certificate information, - // which must be ignored. - // - // set_key_data should return true if the operation succeeded, and - // false otherwise. After the first call to create_session for a - // given policy, set_key_data should immediately return false if - // called. - virtual bool set_key_data(const uint8_t* key_data, int key_len) = 0; - - // set_verify_peers should modify the validation rules for - // verifying a peer during connection handshake. The format of - // verify_peers is implementation specific. - // - // set_verify_peers should return true if the operation succeed, - // and false otherwise. After the first call to create_session for - // a given policy, set_verify_peers should immediately return - // false if called. - virtual bool set_verify_peers(const uint8_t* verify_peers, int verify_peers_len) = 0; - - // create_session should return a new object that implements - // ITLSSession, associated with this policy. After the first call - // to create_session for a given policy, further calls to - // ITLSPolicy::set_* will fail and return false. - // - // The newly created session should use send_func and recv_func to - // send and receive data on the underlying transport, and must - // provide send_ctx/recv_ctx to the callbacks. - // - // uid should only be provided when invoking an ITLSLogFunc, which - // will use it to identify this session. - virtual ITLSSession* create_session(bool is_client, TLSSendCallbackFunc send_func, void* send_ctx, TLSRecvCallbackFunc recv_func, void* recv_ctx, void* uid ) = 0; -}; - -// Logs a message/error to the appropriate trace log. -// -// event must be a valid XML attribute value. uid may be NULL or the -// uid provided to ITLSPolicy::create_session by the caller. is_error -// should be true for errors and false for informational messages. The -// remaining arguments must be pairs of (const char*); the first of -// each pair must be a valid XML attribute name, and the second a -// valid XML attribute value. The final parameter must be NULL. -typedef void (*ITLSLogFunc)(const char* event, void* uid, bool is_error, ...); - -struct ITLSPlugin { - virtual void addref() = 0; - virtual void delref() = 0; - - // create_policy should return a new object that implements - // ITLSPolicy. - // - // The newly created policy, and any session further created from - // the policy, should use logf to log any messages or errors that - // occur. - virtual ITLSPolicy* create_policy( ITLSLogFunc logf ) = 0; - - static inline const char* get_plugin_type_name_and_version() { return "ITLSPlugin"; } -}; - -#endif /* FDB_ITLSPLUGIN_H */ diff --git a/FDBLibTLS/Makefile b/FDBLibTLS/Makefile index 4e0782268e7..c0d05116226 100644 --- a/FDBLibTLS/Makefile +++ b/FDBLibTLS/Makefile @@ -69,7 +69,7 @@ $(error Unknown platform $(PLATFORM)) endif PLUGIN := FDBLibTLS.$(DYEXT) -OBJECTS := FDBLibTLSPlugin.o FDBLibTLSPolicy.o FDBLibTLSSession.o +OBJECTS := FDBLibTLSPlugin.o FDBLibTLSPolicy.o FDBLibTLSSession.o FDBLibTLSVerify.o LINKLINE := $(CXXFLAGS) $(CFLAGS) $(LDFLAGS) $(OBJECTS) $(LINK_LDFLAGS) -o $(PLUGIN) all: $(PLUGIN) diff --git a/FDBLibTLS/local.mk b/FDBLibTLS/local.mk index 0b6eac817c2..5e6b9cfb405 100644 --- a/FDBLibTLS/local.mk +++ b/FDBLibTLS/local.mk @@ -1,11 +1,11 @@ -FDBLibTLS_CFLAGS := -fPIC -I/usr/local/include -I$(BOOSTDIR) +FDBLibTLS_CFLAGS := -fPIC -I/usr/local/include -I$(BOOSTDIR) -Ifdbrpc FDBLibTLS_STATIC_LIBS := -ltls -lssl -lcrypto FDBLibTLS_LDFLAGS := -L/usr/local/lib -static-libstdc++ -static-libgcc -lrt FDBLibTLS_LDFLAGS += -Wl,-soname,FDBLibTLS.so -Wl,--version-script=FDBLibTLS/FDBLibTLS.map # The plugin isn't a typical library, so it feels more sensible to have a copy # of it in bin/. -bin/FDBLibTLS.$(DLEXT): lib/libFDBLibTLS.$(DLEXT) +bin/fdb-libressl-plugin.$(DLEXT): lib/libFDBLibTLS.$(DLEXT) @cp $< $@ -TARGETS += bin/FDBLibTLS.$(DLEXT) +TARGETS += bin/fdb-libressl-plugin.$(DLEXT) diff --git a/FDBLibTLS/plugin-test.cpp b/FDBLibTLS/plugin-test.cpp index 7156d1b48c6..087ca86db3f 100644 --- a/FDBLibTLS/plugin-test.cpp +++ b/FDBLibTLS/plugin-test.cpp @@ -48,29 +48,27 @@ static std::string load_file(std::string path) return ss.str(); } -struct FDBLibTLSClientServerTest { - FDBLibTLSClientServerTest(bool client_success, bool server_success, std::string client_path, std::string server_path, std::string client_verify, std::string server_verify): - client_success(client_success), server_success(server_success), client_verify(client_verify), server_verify(server_verify) { - client_data = load_file(TESTDATA + client_path); - server_data = load_file(TESTDATA + server_path); - } - ~FDBLibTLSClientServerTest() {} +struct client_server_test { + std::string ca_path; bool client_success; - bool server_success; + std::string client_path; + const char* client_password; + std::vector client_verify; + const char* servername; - std::string client_data; - std::string client_verify; - std::string server_data; - std::string server_verify; + bool server_success; + std::string server_path; + const char* server_password; + std::vector server_verify; }; struct FDBLibTLSPluginTest { - FDBLibTLSPluginTest(Reference plugin, ITLSLogFunc logf); - ~FDBLibTLSPluginTest(); + FDBLibTLSPluginTest(Reference plugin, ITLSLogFunc logf); + ~FDBLibTLSPluginTest(); - Reference plugin; - ITLSLogFunc logf; + Reference plugin; + ITLSLogFunc logf; boost::circular_buffer client_buffer; boost::circular_buffer server_buffer; @@ -83,13 +81,13 @@ struct FDBLibTLSPluginTest { int server_write(const uint8_t* buf, int len); Reference create_policy(void); - Reference create_client_session(Reference policy); + Reference create_client_session(Reference policy, const char* servername); Reference create_server_session(Reference policy); void circular_reset(void); void circular_self_test(void); - int client_server_test(FDBLibTLSClientServerTest const& cst); + int client_server_test(const struct client_server_test *cst); int set_cert_data_test(void); }; @@ -225,9 +223,9 @@ static int client_recv_func(void* ctx, uint8_t* buf, int len) { } } -Reference FDBLibTLSPluginTest::create_client_session(Reference policy) +Reference FDBLibTLSPluginTest::create_client_session(Reference policy, const char* servername) { - return Reference(policy->create_session(true, client_send_func, this, client_recv_func, this, NULL)); + return Reference(policy->create_session(true, servername, client_send_func, this, client_recv_func, this, NULL)); } static int server_send_func(void* ctx, const uint8_t* buf, int len) { @@ -250,42 +248,74 @@ static int server_recv_func(void* ctx, uint8_t* buf, int len) { Reference FDBLibTLSPluginTest::create_server_session(Reference policy) { - return Reference(policy->create_session(false, server_send_func, this, server_recv_func, this, NULL)); + return Reference(policy->create_session(false, NULL, server_send_func, this, server_recv_func, this, NULL)); +} + +#define MAX_VERIFY_RULES 5 + +static void convert_verify_peers(const std::vector *verify_rules, const uint8_t *verify_peers[], int verify_peers_len[]) { + if (verify_rules->size() > MAX_VERIFY_RULES) + throw std::runtime_error("verify"); + int i = 0; + for (auto &verify_rule: *verify_rules) { + verify_peers[i] = (const uint8_t *)&verify_rule[0]; + verify_peers_len[i] = verify_rule.size(); + i++; + } } -int FDBLibTLSPluginTest::client_server_test(FDBLibTLSClientServerTest const& cst) +int FDBLibTLSPluginTest::client_server_test(const struct client_server_test* cst) { + const uint8_t *verify_peers[MAX_VERIFY_RULES]; + int verify_peers_len[MAX_VERIFY_RULES]; + circular_reset(); + std::string ca_data = load_file(TESTDATA + cst->ca_path); + std::string client_data = load_file(TESTDATA + cst->client_path); + std::string server_data = load_file(TESTDATA + cst->server_path); + Reference client_policy = create_policy(); - if (!client_policy->set_cert_data((const uint8_t*)&cst.client_data[0], cst.client_data.size())) { - std::cerr << "FAIL: failed to set client cert data\n"; + if (!client_policy->set_ca_data((const uint8_t*)&ca_data[0], ca_data.size())) { + std::cerr << "FAIL: failed to set client ca data\n"; return 1; } - if (!client_policy->set_key_data((const uint8_t*)&cst.client_data[0], cst.client_data.size())) { - std::cerr << "FAIL: failed to set client key data\n"; + if (!client_policy->set_cert_data((const uint8_t*)&client_data[0], client_data.size())) { + std::cerr << "FAIL: failed to set client cert data\n"; return 1; } - if (!client_policy->set_verify_peers((const uint8_t*)&cst.client_verify[0], cst.client_verify.size())) { + if (!client_policy->set_key_data((const uint8_t*)&client_data[0], client_data.size(), cst->client_password)) { std::cerr << "FAIL: failed to set client key data\n"; return 1; } + if (!cst->client_verify.empty()) { + convert_verify_peers(&cst->client_verify, verify_peers, verify_peers_len); + if (!client_policy->set_verify_peers(cst->client_verify.size(), verify_peers, verify_peers_len)) { + std::cerr << "FAIL: failed to set client verify peers\n"; + return 1; + } + } Reference server_policy = create_policy(); - if (!server_policy->set_cert_data((const uint8_t*)&cst.server_data[0], cst.server_data.size())) { + if (!server_policy->set_ca_data((const uint8_t*)&ca_data[0], ca_data.size())) { + std::cerr << "FAIL: failed to set server ca data\n"; + return 1; + } + if (!server_policy->set_cert_data((const uint8_t*)&server_data[0], server_data.size())) { std::cerr << "FAIL: failed to set server cert data\n"; return 1; } - if (!server_policy->set_key_data((const uint8_t*)&cst.server_data[0], cst.server_data.size())) { + if (!server_policy->set_key_data((const uint8_t*)&server_data[0], server_data.size(), cst->server_password)) { std::cerr << "FAIL: failed to set server key data\n"; return 1; } - if (!server_policy->set_verify_peers((const uint8_t*)&cst.server_verify[0], cst.server_verify.size())) { - std::cerr << "FAIL: failed to set client key data\n"; + convert_verify_peers(&cst->server_verify, verify_peers, verify_peers_len); + if (!server_policy->set_verify_peers(cst->server_verify.size(), verify_peers, verify_peers_len)) { + std::cerr << "FAIL: failed to set server verify peers\n"; return 1; } - Reference client_session = create_client_session(client_policy); + Reference client_session = create_client_session(client_policy, cst->servername); Reference server_session = create_server_session(server_policy); if (client_session.getPtr() == NULL || server_session.getPtr() == NULL) @@ -302,7 +332,7 @@ int FDBLibTLSPluginTest::client_server_test(FDBLibTLSClientServerTest const& cst if (rc == ITLSSession::SUCCESS) { client_done = true; } else if (rc == ITLSSession::FAILED) { - if (cst.client_success) { + if (cst->client_success) { std::cerr << "FAIL: failed to complete client handshake\n"; return 1; } else { @@ -320,7 +350,7 @@ int FDBLibTLSPluginTest::client_server_test(FDBLibTLSClientServerTest const& cst if (rc == ITLSSession::SUCCESS) { server_done = true; } else if (rc == ITLSSession::FAILED) { - if (cst.server_success) { + if (cst->server_success) { std::cerr << "FAIL: failed to complete server handshake\n"; return 1; } else { @@ -340,11 +370,15 @@ int FDBLibTLSPluginTest::client_server_test(FDBLibTLSClientServerTest const& cst return 1; } - if (!cst.client_success && !client_failed) + if (!cst->client_success && !client_failed) { std::cerr << "FAIL: client handshake succeeded when it should have failed\n"; - if (!cst.server_success && !server_failed) + return 1; + } + if (!cst->server_success && !server_failed) { std::cerr << "FAIL: server handshake succeeded when it should have failed\n"; - if (!cst.client_success || !cst.server_success) + return 1; + } + if (!cst->client_success || !cst->server_success) return 0; std::cerr << "INFO: handshake completed successfully\n"; @@ -477,6 +511,546 @@ static void logf(const char* event, void* uid, int is_error, ...) { va_end(args); } +const struct client_server_test client_server_tests[] = { + // Single root CA. + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-2.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-2.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + + // Multiple root CAs. + { + .ca_path = "test-ca-all.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {""}, + }, + { + .ca_path = "test-ca-all.pem", + .client_success = true, + .client_path = "test-client-4.pem", + .client_password = "fdb321", + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = "fdb123", + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {""}, + }, + { + .ca_path = "test-ca-2.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = false, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {""}, + }, + + // Expired certificates. + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-3.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-3.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = false, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"Check.Unexpired=0"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-3.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-3.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"Check.Unexpired=0"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"Check.Valid=0"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-3.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-3.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"Check.Valid=0"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1", "I.CN=FDB LibTLS Plugin Test Intermediate CA 2,Check.Unexpired=0"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-3.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1,Check.Unexpired=0", "I.CN=FDB LibTLS Plugin Test Intermediate CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-3.pem", + .server_password = NULL, + .server_verify = {""}, + }, + + // Match on specific subject and/or issuer. + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"C=US"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"C=US"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"C=AU"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"C=US", "C=AU"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"C=US", "C=JP"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\04\\>"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\81 \\<\\01\\+\\02=\\04\\>"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\04"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {"CN=FDB LibTLS Plugin Test Client 1"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"CN=FDB LibTLS Plugin Test Client 1"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-2.pem", + .client_password = NULL, + .client_verify = {""}, + .servername = NULL, + .server_success = false, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"O=Apple Pty Limited,OU=FDC Team"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-2.pem", + .client_password = NULL, + .client_verify = {"O=Apple Inc.,OU=FDB Team"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"O=Apple Pty Limited,OU=FDB Team"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-2.pem", + .client_password = NULL, + .client_verify = {"O=Apple Inc.,OU=FDC Team"}, + .servername = NULL, + .server_success = false, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"O=Apple Pty Limited,OU=FDC Team"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDB Team"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDB Team"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDC Team"}, + .servername = NULL, + .server_success = false, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDC Team"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>,I.CN=FDB LibTLS Plugin Test Intermediate CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1,O=Apple Inc.,I.C=US,S.C=US"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>,I.CN=FDB LibTLS Plugin Test Intermediate CA 1"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {"I.CN=FDB LibTLS Plugin Test Intermediate CA 1,O=Apple Inc.,I.C=US,S.C=US"}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-2.pem", + .server_password = NULL, + .server_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + }, + { + .ca_path = "test-ca-all.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + }, + { + .ca_path = "test-ca-all.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1", "R.CN=FDB LibTLS Plugin Test Root CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + }, + { + .ca_path = "test-ca-all.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 2"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {"R.CN=FDB LibTLS Plugin Test Root CA 1"}, + }, + { + .ca_path = "test-ca-all.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {"R.OU=FDB Team"}, + .servername = NULL, + .server_success = true, + .server_path = "test-server-4.pem", + .server_password = "fdb123", + .server_verify = {"R.OU=FDB Team"}, + }, + + // Client performing name validation via servername. + { + .ca_path = "test-ca-1.pem", + .client_success = true, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {}, + .servername = "test.foundationdb.org", + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, + { + .ca_path = "test-ca-1.pem", + .client_success = false, + .client_path = "test-client-1.pem", + .client_password = NULL, + .client_verify = {}, + .servername = "www.foundationdb.org", + .server_success = true, + .server_path = "test-server-1.pem", + .server_password = NULL, + .server_verify = {""}, + }, +}; + int main(int argc, char **argv) { void *pluginSO = NULL; @@ -502,81 +1076,12 @@ int main(int argc, char **argv) Reference plugin = Reference((ITLSPlugin *)getPlugin(ITLSPlugin::get_plugin_type_name_and_version())); - std::vector tests = { - // Valid - all use single root CA. - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-1-server.pem", "", ""), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", "", ""), - FDBLibTLSClientServerTest(true, true, "test-2-client.pem", "test-2-server.pem", "", ""), - FDBLibTLSClientServerTest(true, true, "test-2-client.pem", "test-1-server.pem", "", ""), - - // Certificates terminate at different intermediate CAs. - FDBLibTLSClientServerTest(false, false, "test-4-client.pem", "test-5-server.pem", "", ""), - FDBLibTLSClientServerTest(false, false, "test-5-client.pem", "test-4-server.pem", "", ""), - FDBLibTLSClientServerTest(true, true, "test-4-client.pem", "test-5-server.pem", - "Check.Valid=0", "Check.Valid=0"), - FDBLibTLSClientServerTest(true, true, "test-5-client.pem", "test-4-server.pem", - "Check.Valid=0", "Check.Valid=0"), - - // Expired certificates. - FDBLibTLSClientServerTest(false, false, "test-1-client.pem", "test-3-server.pem", "", ""), - FDBLibTLSClientServerTest(false, false, "test-3-client.pem", "test-1-server.pem", "", ""), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-3-server.pem", "Check.Unexpired=0", ""), - FDBLibTLSClientServerTest(true, true, "test-3-client.pem", "test-1-server.pem", "", "Check.Unexpired=0"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-3-server.pem", "Check.Valid=0", ""), - FDBLibTLSClientServerTest(true, true, "test-3-client.pem", "test-1-server.pem", "", "Check.Valid=0"), - - // Match on specific subject and/or issuer. - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-1-server.pem", "C=US", ""), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-2-server.pem", "C=US", ""), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", "C=AU", ""), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>", ""), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\04\\>", ""), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\81 \\<\\01\\+\\02=\\04\\>", ""), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\04", ""), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>", - "CN=FDB LibTLS Plugin Test Client 1"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-1-server.pem", - "", "CN=FDB LibTLS Plugin Test Client 1"), - FDBLibTLSClientServerTest(true, false, "test-2-client.pem", "test-1-server.pem", - "", "O=Apple Pty Limited,OU=FDC Team"), - FDBLibTLSClientServerTest(true, true, "test-2-client.pem", "test-1-server.pem", - "O=Apple Inc.,OU=FDB Team", "O=Apple Pty Limited,OU=FDB Team"), - FDBLibTLSClientServerTest(false, false, "test-2-client.pem", "test-1-server.pem", - "O=Apple Inc.,OU=FDC Team", "O=Apple Pty Limited,OU=FDC Team"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-1-server.pem", - "I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDB Team", - "I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDB Team"), - FDBLibTLSClientServerTest(false, false, "test-1-client.pem", "test-1-server.pem", - "I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDC Team", - "I.C=US,I.ST=California,I.L=Cupertino,I.O=Apple Inc.,I.OU=FDC Team"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-1-server.pem", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1"), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-1-server.pem", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 2", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 2", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1"), - FDBLibTLSClientServerTest(true, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>,I.CN=FDB LibTLS Plugin Test Intermediate CA 2", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1,O=Apple Inc.,I.C=US,S.C=US"), - FDBLibTLSClientServerTest(false, true, "test-1-client.pem", "test-2-server.pem", - "CN=FDB LibTLS Plugin Test Server 2\\, \\80 \\<\\01\\+\\02=\\03\\>,I.CN=FDB LibTLS Plugin Test Intermediate CA 1", - "I.CN=FDB LibTLS Plugin Test Intermediate CA 1,O=Apple Inc.,I.C=US,S.C=US"), - }; - FDBLibTLSPluginTest *pt = new FDBLibTLSPluginTest(plugin, (ITLSLogFunc)logf); int test_num = 1; - for (auto &test: tests) { + for (auto &cst: client_server_tests) { std::cerr << "== Test " << test_num++ << " ==\n"; - failed |= pt->client_server_test(test); + failed |= pt->client_server_test(&cst); } delete pt; diff --git a/FDBLibTLS/scripts/make-test-certs.sh b/FDBLibTLS/scripts/make-test-certs.sh index e5e8e46ab07..266e1cd1858 100755 --- a/FDBLibTLS/scripts/make-test-certs.sh +++ b/FDBLibTLS/scripts/make-test-certs.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# make-tests-certs.sh +# make-test-certs.sh # # This source file is part of the FoundationDB open source project # @@ -33,6 +33,17 @@ cleanup() { trap cleanup EXIT INT +make_ca_bundle() { + local bundle_file=$1; + shift 1; + + printf '' > "${bundle_file}" + for f in $@; do + openssl x509 -nameopt oneline -subject -issuer -noout -in "${TMPDIR}/${f}" >> "${bundle_file}" + cat "${TMPDIR}/${f}" >> "${bundle_file}" + done +} + make_bundle() { local bundle_file=$1; local key_file=$2; @@ -80,36 +91,60 @@ subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:false keyUsage = critical, digitalSignature + +[fdb_v3_server_san] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:false +keyUsage = critical, digitalSignature +subjectAltName = @fdb_v3_server_alt_names + +[fdb_v3_server_alt_names] +DNS.1 = test.foundationdb.org EOF -# Root CA. +# Root CA 1. +openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 -x509 \ + -subj "${SUBJECT} Root CA 1" -keyout "${TMPDIR}/ca-root-1.key" \ + -config "${TMPDIR}/openssl.cnf" -extensions fdb_v3_ca \ + -out "${TMPDIR}/ca-root-1.crt" + +# Root CA 2. openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 -x509 \ - -subj "${SUBJECT} Root CA" -keyout "${TMPDIR}/ca-root.key" \ + -subj "${SUBJECT_ALT} Root CA 2" -keyout "${TMPDIR}/ca-root-2.key" \ -config "${TMPDIR}/openssl.cnf" -extensions fdb_v3_ca \ - -out "${TMPDIR}/ca-root.crt" + -out "${TMPDIR}/ca-root-2.crt" -# Intermediate CA 1. +# Intermediate CA 1 (from CA 1). openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \ -subj "${SUBJECT} Intermediate CA 1" -keyout "${TMPDIR}/ca-int-1.key" \ -out "${TMPDIR}/ca-int-1.csr" -openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-root.crt" -CAkey "${TMPDIR}/ca-root.key" \ +openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-root-1.crt" -CAkey "${TMPDIR}/ca-root-1.key" \ -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_ca -days 3650 \ -CAcreateserial -in "${TMPDIR}/ca-int-1.csr" -out "${TMPDIR}/ca-int-1.crt" -# Intermediate CA 2. +# Intermediate CA 2 (from CA 1). openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \ -subj "${SUBJECT} Intermediate CA 2" -keyout "${TMPDIR}/ca-int-2.key" \ -out "${TMPDIR}/ca-int-2.csr" -openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-root.crt" -CAkey "${TMPDIR}/ca-root.key" \ +openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-root-1.crt" -CAkey "${TMPDIR}/ca-root-1.key" \ -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_ca -days 3650 \ -CAcreateserial -in "${TMPDIR}/ca-int-2.csr" -out "${TMPDIR}/ca-int-2.crt" +# Intermediate CA 3 (from CA 2). +openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \ + -subj "${SUBJECT} Intermediate CA 3" -keyout "${TMPDIR}/ca-int-3.key" \ + -out "${TMPDIR}/ca-int-3.csr" +openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-root-2.crt" -CAkey "${TMPDIR}/ca-root-2.key" \ + -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_ca -days 3650 \ + -CAcreateserial -in "${TMPDIR}/ca-int-3.csr" -out "${TMPDIR}/ca-int-3.crt" + # Server 1. openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \ -subj "${SUBJECT} Server 1" -keyout "${TMPDIR}/server-1.key" \ -out "${TMPDIR}/server-1.csr" openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-int-1.crt" -CAkey "${TMPDIR}/ca-int-1.key" \ - -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_other -days 3650 \ + -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_server_san -days 3650 \ -CAcreateserial -in "${TMPDIR}/server-1.csr" -out "${TMPDIR}/server-1.crt" # Server 2. @@ -130,6 +165,15 @@ printf "y\ny\n" | openssl ca -cert "${TMPDIR}/ca-int-1.crt" -keyfile "${TMPDIR}/ -config "${TMPDIR}/openssl.cnf" -notext \ -in "${TMPDIR}/server-3.csr" -out "${TMPDIR}/server-3.crt" +# Server 4. +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -aes128 -pass pass:fdb123 \ + -out "${TMPDIR}/server-4.key" +openssl req -new -days 3650 -sha256 -key "${TMPDIR}/server-4.key" -passin pass:fdb123 \ + -subj "${SUBJECT} Server 4" -out "${TMPDIR}/server-4.csr" +openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-int-3.crt" -CAkey "${TMPDIR}/ca-int-3.key" \ + -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_other -days 3650 \ + -CAcreateserial -in "${TMPDIR}/server-4.csr" -out "${TMPDIR}/server-4.crt" + # Client 1. openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \ -subj "${SUBJECT} Client 1" -keyout "${TMPDIR}/client-1.key" \ @@ -156,23 +200,35 @@ printf "y\ny\n" | openssl ca -cert "${TMPDIR}/ca-int-1.crt" -keyfile "${TMPDIR}/ -config "${TMPDIR}/openssl.cnf" \ -in "${TMPDIR}/client-3.csr" -out "${TMPDIR}/client-3.crt" +# Client 4. +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -aes128 -pass pass:fdb321 \ + -out "${TMPDIR}/client-4.key" +openssl req -new -days 3650 -sha256 -key "${TMPDIR}/client-4.key" -passin pass:fdb321 \ + -subj "${SUBJECT} Client 4" -out "${TMPDIR}/client-4.csr" +openssl x509 -req -days 3650 -CA "${TMPDIR}/ca-int-3.crt" -CAkey "${TMPDIR}/ca-int-3.key" \ + -extfile "${TMPDIR}/openssl.cnf" -extensions fdb_v3_other \ + -CAcreateserial -in "${TMPDIR}/client-4.csr" -out "${TMPDIR}/client-4.crt" + # # Test Bundles # -make_bundle 'test-1-server.pem' 'server-1.key' 'server-1.crt' 'ca-int-1.crt' 'ca-root.crt' -make_bundle 'test-1-client.pem' 'client-1.key' 'client-1.crt' 'ca-int-1.crt' 'ca-root.crt' -make_bundle 'test-2-server.pem' 'server-2.key' 'server-2.crt' 'ca-int-2.crt' 'ca-root.crt' -make_bundle 'test-2-client.pem' 'client-2.key' 'client-2.crt' 'ca-int-2.crt' 'ca-root.crt' +make_ca_bundle 'test-ca-1.pem' 'ca-root-1.crt' +make_ca_bundle 'test-ca-2.pem' 'ca-root-2.crt' +make_ca_bundle 'test-ca-all.pem' 'ca-root-1.crt' 'ca-root-2.crt' + +# Valid client/server from intermediate CA 1. +make_bundle 'test-client-1.pem' 'client-1.key' 'client-1.crt' 'ca-int-1.crt' +make_bundle 'test-server-1.pem' 'server-1.key' 'server-1.crt' 'ca-int-1.crt' -# Expired client/server. -make_bundle 'test-3-client.pem' 'client-3.key' 'client-3.crt' 'ca-int-1.crt' 'ca-root.crt' -make_bundle 'test-3-server.pem' 'server-3.key' 'server-3.crt' 'ca-int-1.crt' 'ca-root.crt' +# Valid client/server from intermediate CA 2. +make_bundle 'test-client-2.pem' 'client-2.key' 'client-2.crt' 'ca-int-2.crt' +make_bundle 'test-server-2.pem' 'server-2.key' 'server-2.crt' 'ca-int-2.crt' -# Bundles that terminate at intermediate 1. -make_bundle 'test-4-server.pem' 'server-1.key' 'server-1.crt' 'ca-int-1.crt' -make_bundle 'test-4-client.pem' 'client-1.key' 'client-1.crt' 'ca-int-1.crt' +# Expired client/server from intermediate CA 1. +make_bundle 'test-client-3.pem' 'client-3.key' 'client-3.crt' 'ca-int-1.crt' +make_bundle 'test-server-3.pem' 'server-3.key' 'server-3.crt' 'ca-int-1.crt' -# Bundles that terminate at intermediate 2. -make_bundle 'test-5-server.pem' 'server-2.key' 'server-2.crt' 'ca-int-2.crt' -make_bundle 'test-5-client.pem' 'client-2.key' 'client-2.crt' 'ca-int-2.crt' +# Valid client/server from intermediate CA 3. +make_bundle 'test-client-4.pem' 'client-4.key' 'client-4.crt' 'ca-int-3.crt' +make_bundle 'test-server-4.pem' 'server-4.key' 'server-4.crt' 'ca-int-3.crt' diff --git a/FDBLibTLS/testdata/test-1-client.pem b/FDBLibTLS/testdata/test-1-client.pem deleted file mode 100644 index 085f7e35ea4..00000000000 --- a/FDBLibTLS/testdata/test-1-client.pem +++ /dev/null @@ -1,106 +0,0 @@ -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Client 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIJALOPTrQGpeslMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGIMQswCQYDVQQGEwJVUzETMBEG -A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB -cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM -UyBQbHVnaW4gVGVzdCBDbGllbnQgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALVkdxOmWcd959NyirJ1iz7q5fkjdRUV+88KMMehQWc3f50GJIQ+eZo+ -7RhwVE+n8nd0i5iGfyY6LRuupdwoQUxoZ/5rUIDGKspNO62DVRW+tZqzpEa1+ub5 -75BMoc7I7l9sXDkuiMu1OYcPNKMv4F3mf+B3ourLqjUekKlUv8XIZXAvN+R19HlR -FM8vs8rnhQXx7iWVP91frDvyD8G7lOf6R7R4homnB37kLom8WU+fCmcyA6em0qX0 -JeVP6xk2qXU1cMs7DL8WftdrWHv+a73/l4hytQHo5OvtGaLZhpPYpC/FMSaFHVSM -irWSFK+ZtvaLi3LXc2HGANMokjPoRf8CAwEAAaNgMF4wHQYDVR0OBBYEFPtTL9KZ -jn49cLediy1ixz7AXOI3MB8GA1UdIwQYMBaAFCXTF7f83Hd7xm9gR+O4QrvjNo8Q -MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB -AQA17a4d/tSWIlTkIfkrXziD21+1OsN6/dUrWQK7kxtEe21QXIutccW4bwpM0JDB -M+bZiWkdgQ15+ZotX5UXlBcx9WWDU5RqSO06hhXu5b8gZwfVF4Od6tBdVxkn4KbU -0YujOZrL8fDOrQHqCO7nhNlYgcEn7bKF5wjtOoiKhtA9sLSIZQR5g32kkJXXGvcY -lLWMXygEg9FMQoldW9RHq4GbUiYEeqEq6k4S7cE03R1lvmQEOOAJ2S7LnaS4UHQT -GmW6uvLnJJrG4HB9JGE+y1e9M+C7Enzhi39RGd8ylignGimkdw/1UEWnvKGCqoU7 -ufWGF7eUV8dCqO+jYghIY8rA ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1ZHcTplnHfefT -coqydYs+6uX5I3UVFfvPCjDHoUFnN3+dBiSEPnmaPu0YcFRPp/J3dIuYhn8mOi0b -rqXcKEFMaGf+a1CAxirKTTutg1UVvrWas6RGtfrm+e+QTKHOyO5fbFw5LojLtTmH -DzSjL+Bd5n/gd6Lqy6o1HpCpVL/FyGVwLzfkdfR5URTPL7PK54UF8e4llT/dX6w7 -8g/Bu5Tn+ke0eIaJpwd+5C6JvFlPnwpnMgOnptKl9CXlT+sZNql1NXDLOwy/Fn7X -a1h7/mu9/5eIcrUB6OTr7Rmi2YaT2KQvxTEmhR1UjIq1khSvmbb2i4ty13NhxgDT -KJIz6EX/AgMBAAECggEAEm2Mc2CZCl1OKfsfABZU+SVgC7mAcY30MQp1/jHxtQy8 -WDWBjDXUoMj3yV3QEu+bAGvEqtAvJrEOWBucGgu05pBM0FoSqaJ4QmkqQOxwvm7L -gFXzwINIZCLMJbrDTYC4RtV5YQ3LM/bLS19OF64Lez6piyJcWMIsHo1mYO2NNgiD -7f1x1uQw46Q0YHWeoHY58MPfmgfKsqnJDWc8cCuU9fJOWeU4dVrfW8dh9WVAoLZ7 -qAM5vvap11Qk8RXaRnmLjxN6H1M7iVNfcLVNKfG6XOBBepYjZr/qMkuN3ONuqBHl -fC3Zia2zQZRfiuPspX0KhjCfYAKbIZC6oyrQM2uXgQKBgQDoD5voZiCOeGXJEMUk -9JV4V8A96aE0xxy+OHMogVpysxBO4V0Nh0krSLTt9NXnpjawZQ+3pLQ4+2J2XM2e -fJuJJ7Z+Mhjv6epnMM7FoxK1VF7oe+LE7Yk/kg/moCuVS/XhLdQrhZVBJhfEADS3 -oFybf7Q6rJYtN3OYsiFymyneHwKBgQDIGsY5kGdmx27LS5rPMwdw632TF8G5BGbu -C3ty7LYkOWb/9/V4cuWjW4eLJQqCWbJQrzOvg0coxwXLUuEQik+IP2IkF0YlRS43 -VJuULwOxi3Cbj51RoapHhmYTO9fe2A1N9oJMAqEUHY1q/r9txPcguRWyuH2Yv9Ih -OzHnc2DcIQKBgQCGW0MxMq/2zM5hs0vxMYq4ulWbgwDKxd1mZNiHwxzS+8mdYe22 -P3WlkdrvSqnuDNXtGxYWhU2zEBjZ3rFN6WdD6bJHLkox3YTRafjNhLT4N3kbsV6C -FeU44SBDrsiNEAWz8gy9hgH8TknEOTpMdpQnk7CNqA7q7wgGiFvFNwDukQKBgG7i -R03Gs0XE5aRJtPN0N39fPyqvU24O/mqSekno2dWg6W6WHLQuFwo6whVc5UHuKl2D -eISdnmT+RDuzJXxg6El7tgqByyEEAOQwQjYPB2Du/+tz3Z1KlG0mEJI/6xNVbany -G6m7Gz9mUOMlXzaYmsjLRzbN/OsUAIDhqHm0+cuBAoGAZCND80akS3xr3yC87GyX -aA0RoHXbdB6dbP8Y6XYDXR4QFIA4kXwY5cCLaZA/0hP5FOzDhORmaoaPM8vUdNyb -IYvbw2H6tODiU5oICWY6+HQQ2nXikucI4HDYDLbsiV2htZkEmBYWLilYq0Tb8jC5 -u+ehIIvZYLqKaY1GaKmF86A= ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-1-server.pem b/FDBLibTLS/testdata/test-1-server.pem deleted file mode 100644 index b280c8c6417..00000000000 --- a/FDBLibTLS/testdata/test-1-server.pem +++ /dev/null @@ -1,106 +0,0 @@ -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Server 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIJALOPTrQGpesjMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGIMQswCQYDVQQGEwJVUzETMBEG -A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB -cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM -UyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAJp26QAmlMusO7C8Py/I117r3kHvB+My5kIrj8g9sKpktwTzmsJGpvJU -EaKISEdBsJHLGnZJhwIhr/+MG4WDEM4oFNCtBQZznV3wjIQWq1w4IO8/f3+nBPpW -f14fjs1E911Uo/ZOL9bxvh1SIHkS6itgJi+tgVPx7C3s3W3mC5nU3omsE+Rx4DDm -KUq1kyN1ELBIAceQ4wTmQ5B8dv6MSW7zt8Jdrhfhg2GJIPPB6XUZJ2yIOvgu55GW -J5sMPa0uNDfCsWJ37fzFm+XJ/D96t7x8I49IyfzbIgcU9JYFlcqkryvKh5IpQGGm -H/I6adIWa5xWpMhB2PA6kgtDD07Hu2sCAwEAAaNgMF4wHQYDVR0OBBYEFJ7S+FUz -9ngzH/TNPVeM/cE7LeBGMB8GA1UdIwQYMBaAFCXTF7f83Hd7xm9gR+O4QrvjNo8Q -MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB -AQAx6WHwikVFAH0TRYCznwO6He+0t2pnlyfrI+24N28tzupMSrRPs086UbLgHLz1 -lbkYdheeOkLPzjWi5vfymL1Oua3E2iAXWEpMb4Sg7E5SVHp9yt6gZ0DTVwR+Gcu7 -uooroidAG3OFeOXL5ivU5J5ipaoEAiLprpKxtPzo4z/TxIqw3kJISC56qw9VTJNQ -TQZvneUecykdIZuH61ih0cJLe5WRkEs/63Dgl8TBYiVDbvBSGRbsXoAXcspVlc2x -XOLey5IVJ4/TH5ZBobShC6J1KrjZTNYvUgc44CocOgrc0ePPiQzB7JXxR1H8ATGl -yKjWqT2PkrfHmjdcmsi2GIVt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCadukAJpTLrDuw -vD8vyNde695B7wfjMuZCK4/IPbCqZLcE85rCRqbyVBGiiEhHQbCRyxp2SYcCIa// -jBuFgxDOKBTQrQUGc51d8IyEFqtcOCDvP39/pwT6Vn9eH47NRPddVKP2Ti/W8b4d -UiB5EuorYCYvrYFT8ewt7N1t5guZ1N6JrBPkceAw5ilKtZMjdRCwSAHHkOME5kOQ -fHb+jElu87fCXa4X4YNhiSDzwel1GSdsiDr4LueRliebDD2tLjQ3wrFid+38xZvl -yfw/ere8fCOPSMn82yIHFPSWBZXKpK8ryoeSKUBhph/yOmnSFmucVqTIQdjwOpIL -Qw9Ox7trAgMBAAECggEAOZAMvsCh/NDfobpVddJL6JTPzBRvBQ1H3+rp9z5+ItHL -nq3Fw5aeynnn5IETJnLlgT+GSgSWqoWxV/N3oia40YsATs/bqo7VW1e0ldj43TIR -m/c25XRxl3U6m/H4vqhv4rkTLUvv6hNGvRiI/3W8DJQVRvlK0+S5FlhKIJV1R0sH -tp5vmaPp09Ln+NVno3u3iaYkVgVME4Ukul2i03sQ9OgvZSBCaVr//fMpiPdBeeN6 -QY6XHjeGQRnP/UdzMYJ4Qz1yovL1ntneaTMdz/GkKuAFoNNh8Vr2kiEskW17OWPB -ZGcIT6YpBEPo34xXUhUQt7ylFPxGH+zZyHZ3vb8j6QKBgQDJPeu/iPg+M5nz5gO5 -ge9gzYrhxK/1mwbFlD7qt1NjOSm6xWxUcss3STjuG7jB0c+NopIUoq/egsUnxrRm -4l17uOCYNLbhTJ2ynfv6QnUMxW5Xkve3DkLa2bze/fhMUywTy8N4A7z0+y35qzm3 -lY4rLmQOQKPkmqWRnxU1u8fjFQKBgQDEfpOZ0fp2D/1gTG+D+/zrMEbjnNn3ZO8I -wrjoXwRxcRggt7lJhxgQpwtDr98IqYkDzX7bvyMFJuyTii3NM6NYycpA1pHX70B/ -xMvOcrgJnIUAoJ7nl43Or7s8bFTPDLaD9PNGHjrlkF3JOXqSKEbw367jHVOa4SYr -OjrogjrEfwKBgQDHU2a7ax5+9btqggx0ZQfGOTBzmM60lZ3qe4CqGXUl1YvIrB01 -tBImq4cRCTJB/9/1qO3KNK2/1oUTddRgB5ySnDcRaz0tASc9sQ/Q/JxVTwSRB0gG -78A2Zu6VbLbQWp1Q6kWtDP7PJC+QmRFtDlwn1yZRm6L6HlcaWpi2hU1iVQKBgCEu -ashv8Aad3qCzZ6V3GReyOFZZd2lSjxcAou8ClKJ/gZ6Mx+pFuOee/cT5XwV8c5nD -yuda2JQXJZ4omGFtlej5coEOeuRnD5JD7lK3hqKA3ujjNtJPAnBjto+Wj5/DOtL/ -u1Ec6782aNABN9SUnp4wd7z8h9DAsoxcMfRvgXMLAoGBAJ9gGttfqZbuPz9V0rAo -p05SPPado1i5+2dUOScIbNB6+vQij9IlR2Tzu1T9DwzrBqTDPPmSggeA/JXeTvh6 -Skb9fDukizeDfwPYUN2gljhiJEqFdpRBr5vP0lFi291+a0jMW1zldrumxCcGKMyU -D5ReKLp/zSQSQi/Wt4FF1II7 ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-2-client.pem b/FDBLibTLS/testdata/test-2-client.pem deleted file mode 100644 index 25083c63d44..00000000000 --- a/FDBLibTLS/testdata/test-2-client.pem +++ /dev/null @@ -1,106 +0,0 @@ -subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Client 2, \C2\80 <\01+\02=\03>" -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIJALOPTrQGpesmMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGdMQswCQYDVQQGEwJBVTEYMBYG -A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM -EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr -RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBDbGllbnQgMiwgwoAgPAErAj0DPjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALanLExQi/yK2PRyu6Mvdw2MRxUs -26kJftYuK7KtYyltTO3vtj4kNKg1vZI1eezhqr2Ta/1DzE76eLVs8EOW0LAb5oWM -zXdYBXBX4vG+K7pYfjuvZUd6jfX2bHW10xC96HgDTfRn6dof8GR0fILJ6DoEcyI3 -82xnKKxTsgAuXU4uvcsl0g0F78nXuIbk8ZktTV3LIdbOCIcLQfG7DdDyAfEA0T7Q -Vg6eeLknIUvPePxyWkUdYeSCDP2d+3NIlHMxNPmH1q3+fCsEsy/kqdVO9e6KrZla -CKqnc6yYTXvTffpPepC3Igz678iGg3dv9rLj0i4fyTr4tEOTJebO9Ka3TbMCAwEA -AaNgMF4wHQYDVR0OBBYEFKO2/D1IhG8KWFwR6OdyoFqEzIWAMB8GA1UdIwQYMBaA -FJFP+HFpDrD0BRU0yE606s6xkqFBMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD -AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQAQV3FjsvZvwi5Oi/oSc7Du/BQS9nQ/D4j6 -IeYpd3M0y50awZB83BReYrhdC907xKkLRD0R8oEPDEg5SaSj3vRML4kaUUqnEINW -4JQtv4wNO9CagYriGg8ygQa0xd683svHeXDet3ov11XN/Ms8lfDiOUp2291HgeTW -8hqn1DaNfZrCb3EkdoNThwVKIUzQtEPBuPkLE+XT8kZP5d8KHmv8/9L39NdZY32d -fzKGBeCxZ34pQS0cTap3rZ02nDfV2vNevODRyuqdhs7EQps2Oe1IfPB9GSE0OFUQ -tdphxSjsv1BcHpTwBDpIITKarnceMIKxQjcZU3yPv5ibIaGCgZOt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpesiMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEApTSBCiUb0amf+QRV2WY6b3bK93D/PSrm4KR/2m2V0lciU1DAk00/kZ52 -ZIZmq8g9EaE2+CaDtU0fMvDZpaZD+vTFRwsx4varehq0ZwX9Wt25i/3G/eGLNlD3 -9E4tDNruK5UQjum4nJ0SV+AdFEGkSfeU3ZJEHYH0NrcbyAUbh0KeWCSwHiYiFJJf -gBYwRq/HdKNoS/4YvLXzTLR7BSm3YcqWlO5tdkJ2lcT/7Th/Hq1TCW/FKwdQJJBq -JrbOYGlMrf1pLO7Drei/xhsYkwTQ899MhSjkBRhc+401p41Mky0n8wLkuPJGhoY3 -9QUOjT+Rmvq5yryg0eWGiFquk6Ru5QIDAQABo2MwYTAdBgNVHQ4EFgQUkU/4cWkO -sPQFFTTITrTqzrGSoUEwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAJfCHU7dm2/2ASyt3wyxivQLxlV6FsEZcF7HcpbbxuB73frGOL4kEoOxvr2X -fBGyjlPMotbc1MeAalAv+hVHdcAcBFPF7lxtYiV6D7YI5T5yVbWSASG3+DMAiW6S -GdQi2eyeh00nH7Y1IkW+yaky0enBtWLzrw+XzHl6xT6DIEJnir//PNxvgXTJ5sjk -6eFAm8HJIqkNQmgfChMQfUH6nm66WwULW6I117RCSkXhIgxZ7wzDq8bXcEdXCrZk -yy5ket9OiVpbd38JgdYirBLmCQVq0uDOOPLz4ZJmNCzQzEt+38AAK2azAk/eb8W9 -JaKWH+5V8lhlyGw1zQKdNEP/wg8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2pyxMUIv8itj0 -crujL3cNjEcVLNupCX7WLiuyrWMpbUzt77Y+JDSoNb2SNXns4aq9k2v9Q8xO+ni1 -bPBDltCwG+aFjM13WAVwV+Lxviu6WH47r2VHeo319mx1tdMQveh4A030Z+naH/Bk -dHyCyeg6BHMiN/NsZyisU7IALl1OLr3LJdINBe/J17iG5PGZLU1dyyHWzgiHC0Hx -uw3Q8gHxANE+0FYOnni5JyFLz3j8clpFHWHkggz9nftzSJRzMTT5h9at/nwrBLMv -5KnVTvXuiq2ZWgiqp3OsmE170336T3qQtyIM+u/IhoN3b/ay49IuH8k6+LRDkyXm -zvSmt02zAgMBAAECggEAU2sYHSZwOH+FRGcd8RJdcg+N60rYa2QNzG27wVfUwPfN -OaHP/qN0dRpOIPdRXvFVlE0+9aVAKxXTiTBers+zMascZgP/VrEZksxgtn1e5TVD -OakKPVHogdvwfvXylmPVRvJjaOsIb3lExew5bVYfPFgJ6Sfagbi/Z6y1z8VdEbYb -mI34KSZA4bBAMAHPZLa9TGEx/vbPsBlqpU6k8lcoy3cTkO5fCZW4ZZIpwBwef4uJ -UozhRgtTtRBiUpk0F9IoOXonZY1Dtpg+HcDMti/FYgahBVe1hadJ+lbVTxH6GxyI -NJYvptdq5S99UOoJDmCCih0v0ZCUNYWoO0I0vzNncQKBgQDemN7es2fIBstiPjOf -p103DF5j9Uxq5YH9B3wli0CXf6Z2w5uosONoJWgJZKsHJ6f+YSuHsoE/eCrFF3U9 -lxT9Nie/wYYIGedly/VR143aCdiTXI44m5gxXgwaUcjvY1DpWyEAAmr5XNdoyZ5n -LNTvOTb4vVo9SgDU7II7rdpRmwKBgQDSD9aBtIy/650suQK/9RiXRU0Kg7LXXVM5 -lavPgLvH55lufJeGSa8+ofCNeo31N4AaVuU4lkGeny9tLNBQbYAoyAz0lf51qK7B -1u5JqBDyRrIpdkqwbT0FT1pu1LA3+Qg0KQBrTCnOx+YyyVSivR4YMZzJjmwZGKMg -BWOi0PzhyQKBgGR44dfpaIWbs39zjf+ZHnTza0N4+/YgA60/DKUxloULRArFPeRF -e0+N2siqnJvNJYGnQGuugbIxPjTZ4rxbDklAgW6HHkVX099Z0TAQuGFbIltZYoRg -jrBxv8q9cZHD5Uh/LoT/kmNdqYkNwCbX0IDt9UcOyMVzOq7g1eO0FB/TAoGBAMaG -tWIsMwGHOip0SAcHKtB8bI1NXo5v4yH/NDuOHOqXFcj383S02uzEu8XaV6Ozalx6 -V3SdfTLem0IBIneApajlOGlIAQ9N9qu358ixECMJcYQCCiCnfQ4xqvQoCss7judN -ANpnRvPotMS2xkhvl6uh594NvlgRksnGjh3oibcRAoGBAJKiu5ajmIkelzAhFMEC -Slxhg/E+djJ1/SG/FaF8zIyTOxre/QUvmTwFKtHe6A5EfKQo9GCTuHuAcJ1U7eQP -l2BoY0POqJFpw3s/QOt4g/pOz0YjD9GD6awL5WDfO++s4mnI1Snc3wcu99N4Klax -htsaEUECJBUF0ZpIFad73s2f ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-2-server.pem b/FDBLibTLS/testdata/test-2-server.pem deleted file mode 100644 index 788cf3c217a..00000000000 --- a/FDBLibTLS/testdata/test-2-server.pem +++ /dev/null @@ -1,106 +0,0 @@ -subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Server 2, \C2\80 <\01+\02=\03>" -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIJALOPTrQGpeskMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGdMQswCQYDVQQGEwJBVTEYMBYG -A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM -EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr -RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMiwgwoAgPAErAj0DPjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALAolQZIGMeL5w/Bu2X6lHWjO58u -+HUDtBmr37So4jazhZBSFDBg+QlRMiYGLev9EhvCrUsVcRwtvtcuMI3wfKl7qgbi -ZX8zmrzZ3YJo9U47NzCa05faOl8uSBvuXuXUBLU342WFP8XDB1W8yOBQMK73xoFv -DkcxURx9ZtOhdC3EgYKrFqOB1Azl1DB4gLV3h9rHW5QpQ8SqD9CyggcDBpDeZQIP -+4l5YFE9Nb4kEUTscz2wGn4TdHMmcnVpfUxp1Y2o8Umvh4llXHIPhximGb3JJ4QQ -Sir4ZXeeoooWoJG0sdlqVLroKav/VMGtEu9LyfbrNdKnTJq3ceVQ+HJ2hlMCAwEA -AaNgMF4wHQYDVR0OBBYEFH61Z8O9vFsVdhM4MBU3poX2UMTEMB8GA1UdIwQYMBaA -FJFP+HFpDrD0BRU0yE606s6xkqFBMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD -AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQCVbxlLGIBCo6/XXjqoMyZc7uQZJj7pGnwh -nIMs2izCLfax8j+QrThO2Qjn03zT/WF8eG6ibPbjgnw3VFwCkV6oQ+BXG6Yt0xqP -4rz1LzxSio6HSm26gSk4SQUsVoAtz3OImoTCFVfz+Mixe87pyVXXEEtCYvfU74H9 -I1WGyNkWAxiJbqeIxF5PKoc3EdnT5mfdC6sdeGm7t2neeS8PDFQtJ4UfVIEK5z1C -MOfQILNkLX2nBYxNqKpV66zf68VZNN9002ZH2FITGqImpj74BEws3sheiuZySdoI -wnAwRnymIMfAmkf9C7Q2ugId0YMMyesaWrIwSlXlJOHGsA1VrBRD ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpesiMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEApTSBCiUb0amf+QRV2WY6b3bK93D/PSrm4KR/2m2V0lciU1DAk00/kZ52 -ZIZmq8g9EaE2+CaDtU0fMvDZpaZD+vTFRwsx4varehq0ZwX9Wt25i/3G/eGLNlD3 -9E4tDNruK5UQjum4nJ0SV+AdFEGkSfeU3ZJEHYH0NrcbyAUbh0KeWCSwHiYiFJJf -gBYwRq/HdKNoS/4YvLXzTLR7BSm3YcqWlO5tdkJ2lcT/7Th/Hq1TCW/FKwdQJJBq -JrbOYGlMrf1pLO7Drei/xhsYkwTQ899MhSjkBRhc+401p41Mky0n8wLkuPJGhoY3 -9QUOjT+Rmvq5yryg0eWGiFquk6Ru5QIDAQABo2MwYTAdBgNVHQ4EFgQUkU/4cWkO -sPQFFTTITrTqzrGSoUEwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAJfCHU7dm2/2ASyt3wyxivQLxlV6FsEZcF7HcpbbxuB73frGOL4kEoOxvr2X -fBGyjlPMotbc1MeAalAv+hVHdcAcBFPF7lxtYiV6D7YI5T5yVbWSASG3+DMAiW6S -GdQi2eyeh00nH7Y1IkW+yaky0enBtWLzrw+XzHl6xT6DIEJnir//PNxvgXTJ5sjk -6eFAm8HJIqkNQmgfChMQfUH6nm66WwULW6I117RCSkXhIgxZ7wzDq8bXcEdXCrZk -yy5ket9OiVpbd38JgdYirBLmCQVq0uDOOPLz4ZJmNCzQzEt+38AAK2azAk/eb8W9 -JaKWH+5V8lhlyGw1zQKdNEP/wg8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwKJUGSBjHi+cP -wbtl+pR1ozufLvh1A7QZq9+0qOI2s4WQUhQwYPkJUTImBi3r/RIbwq1LFXEcLb7X -LjCN8Hype6oG4mV/M5q82d2CaPVOOzcwmtOX2jpfLkgb7l7l1AS1N+NlhT/FwwdV -vMjgUDCu98aBbw5HMVEcfWbToXQtxIGCqxajgdQM5dQweIC1d4fax1uUKUPEqg/Q -soIHAwaQ3mUCD/uJeWBRPTW+JBFE7HM9sBp+E3RzJnJ1aX1MadWNqPFJr4eJZVxy -D4cYphm9ySeEEEoq+GV3nqKKFqCRtLHZalS66Cmr/1TBrRLvS8n26zXSp0yat3Hl -UPhydoZTAgMBAAECggEAVD60NlLYduXzVNfDtVuHEFNGOjSOYfepc/V8gLubo6lr -IMAAI7rcnpYUM5cU8x0OQfRyR8wzUdSWxfWzBs6R78PSZoRzIcgeIl7Wzn0/g3BS -To5czuxwqgBKQAFZpPQmZDwcJfr5qqxAn8IvFweCoMqiRlhELcvqDIP0XxWBqDjc -TNJ988XzZXQmJbjjpWOkUBy2Uqz8lZt8MmxKFpW7SW4tBJwPphnorgjWfjCV/VEh -ORio0rG74NHFo4f1TSrdU2BcB2cbVJ4B+bcUYRdvYmS5bmokhGF8vir0l43gUEdz -Fyk6MaPrTI6cinqzenm3q/0eRvNhBE56U0tiGLn14QKBgQDkCkt1Y4LEboSwsVYl -IXriStqj9p9MOizihh0enhzRXTTQuLX82fNi+bh1LAluwv290Q57pvKa+hB/YciB -o4s7QfSojxQY9DxqvXN7CvxPWXHTyFY5sL4Rm807+C/a9rd39MxBynz9u/7YRvsA -s8v8Y/01qIHnTo+mpDvu6HttWwKBgQDFwdRkgstuE+dXZZe8g1ivh3RNPa968TE3 -b8rzF9/nOJV7f6B/n6YEmHD/cHF5mm1bR+zt/jtf1NCRMpazchw3vT3JzQZYMDnM -SD6vxTs5rG47QLiNyTIRmmD4gsEWBpyvoyP8E/9QdfDT1bWI5zZnky9CquRlN+cu -J1bTsefEaQKBgGJsRxFNd91MThztDV9NSfptkFyAT1TZLxI+DEdwusNqVSdY8cNG -VpP7cC+yaAfURSwuFPAtqDxXfdNc4uuBKNDUsMInrubuUz1Gs5cBsNCWrFhZ+U1B -CWgUNMqTXiRFo/40PAyRVs003NOAH0m4UGyIw3rrVdX9xGaKMAv3b35NAoGATkkl -I4UDs1f9xQNaxi3Y9ePRjqJUzX6d1SxUU1eoM4ia5IDpsJwqxLb0RKrmwRT5JaGb -kbuLFazRxCkar38E3Kv1weWAFXlB6DTRXBPgFjzEhoBgjwCO6ZkLulVIysdjT8Rt -gmUINXn7FGENtFyTlP0XQHUWZVt0ETlRjgxni8ECgYBYv6MoSr0iPjQpxeKvwFDz -d9zE+ZXN+3GwtkI340lKRSc/f0Uq1TlC2w+DzjyyXcrBwubMQKTKcQQSH9f3YbMu -DuxVE9AXdlQ1gSQHGjS0qUWwsS/8Xcjk8ZuduAXPGr/MsvsW+FbbZqG8qdZTeMHu -MSTpOxu9HXC8SHML+y0cpw== ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-3-client.pem b/FDBLibTLS/testdata/test-3-client.pem deleted file mode 100644 index 959e3c34fd0..00000000000 --- a/FDBLibTLS/testdata/test-3-client.pem +++ /dev/null @@ -1,150 +0,0 @@ -subject= -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -Certificate: - Data: - Version: 1 (0x0) - Serial Number: 1048578 (0x100002) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=California, L=Cupertino, O=Apple Inc., OU=FDB Team, CN=FDB LibTLS Plugin Test Intermediate CA 1 - Validity - Not Before: Jan 1 00:00:00 2017 GMT - Not After : Dec 31 00:00:00 2017 GMT - Subject: - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:e4:6f:67:4b:e7:d2:1b:0a:ec:f8:92:ae:1e:d4: - e9:00:6b:47:83:ad:4e:9e:e2:cc:52:b0:8a:04:46: - 57:1f:f7:32:37:cc:f0:cd:ec:c0:b9:b9:27:b4:19: - 33:a1:21:a7:4c:a2:6c:c7:56:31:c4:6a:4f:5f:fb: - 92:6c:22:8f:c4:eb:3f:d1:2b:06:c7:7b:6b:90:83: - 37:d3:59:1c:c0:da:de:85:a1:dc:e6:9d:e1:d8:fc: - 6f:d4:c0:b4:6e:37:3d:d2:d7:4e:4e:04:09:6a:fb: - 9f:d3:cf:b1:80:db:7a:78:97:65:e1:bc:8d:5a:fa: - ec:b1:b6:ee:3f:c9:03:83:ab:0a:9a:8e:03:29:88: - 42:14:50:80:11:a7:d5:2d:87:c8:bd:25:32:9e:55: - fb:22:ef:9c:64:a8:a4:62:3b:d6:86:43:1a:22:a3: - 1c:4b:ee:af:30:70:d3:9c:aa:da:b6:87:61:78:87: - 32:0c:0b:b7:44:16:9b:44:1b:4e:6d:f3:98:99:f8: - ed:ae:41:02:5d:52:9a:98:49:c3:24:24:0b:18:7b: - bf:40:ce:37:65:0f:32:0c:1c:5a:47:4b:b0:3f:db: - 17:b6:89:68:99:3c:0e:70:84:92:5c:33:cb:6d:2e: - 67:c8:af:47:41:87:bd:37:87:88:00:65:1f:7e:7b: - d9:09 - Exponent: 65537 (0x10001) - Signature Algorithm: sha256WithRSAEncryption - 0b:5a:f7:7e:e5:93:4b:25:dc:01:eb:20:37:cb:bd:a9:71:3a: - af:6d:73:d9:9e:3c:8b:5c:6d:74:45:76:72:02:64:7e:e6:41: - bf:29:d7:cd:f8:a7:2d:87:32:6d:25:3f:14:11:2b:95:5a:2e: - a8:8b:ba:b1:f9:52:79:b4:5b:ea:fe:b0:ee:b0:9c:14:53:ba: - 5d:64:aa:b9:d9:ca:17:b2:99:da:34:18:31:56:83:d9:21:8f: - 20:9e:6a:7f:09:41:2f:36:fa:ab:e7:d1:6c:76:50:d4:51:69: - b9:93:ae:9a:eb:8a:6f:a9:91:21:58:a9:3d:53:e8:c1:2c:6f: - 88:25:65:03:8a:90:9c:8e:58:5d:9a:e2:67:8e:6a:f6:11:19: - 24:8d:89:b7:11:5e:a8:dc:21:35:7a:9a:78:8a:94:c2:29:84: - bb:b7:a5:8e:04:79:dc:db:9d:d7:a7:a3:b7:39:e6:c3:a5:be: - 83:ad:59:3a:ee:ea:4a:8a:bd:6e:71:c9:e4:a7:46:d5:a3:fd: - a0:b1:a3:54:8d:bc:01:fb:68:4c:5a:a2:f5:79:44:f7:b9:e9: - 7b:db:91:91:74:5b:68:f6:3a:b2:70:ee:e6:49:f4:f1:a6:53: - 66:13:ce:2f:9e:88:45:66:34:ae:fc:0d:14:02:6f:6a:c9:ac: - b5:3f:89:bc ------BEGIN CERTIFICATE----- -MIIDCDCCAfACAxAAAjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UEBhMCVVMxEzAR -BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK -QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU -TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwHhcNMTcwMTAxMDAwMDAw -WhcNMTcxMjMxMDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA5G9nS+fSGwrs+JKuHtTpAGtHg61OnuLMUrCKBEZXH/cyN8zwzezAubkntBkz -oSGnTKJsx1YxxGpPX/uSbCKPxOs/0SsGx3trkIM301kcwNrehaHc5p3h2Pxv1MC0 -bjc90tdOTgQJavuf08+xgNt6eJdl4byNWvrssbbuP8kDg6sKmo4DKYhCFFCAEafV -LYfIvSUynlX7Iu+cZKikYjvWhkMaIqMcS+6vMHDTnKratodheIcyDAu3RBabRBtO -bfOYmfjtrkECXVKamEnDJCQLGHu/QM43ZQ8yDBxaR0uwP9sXtolomTwOcISSXDPL -bS5nyK9HQYe9N4eIAGUffnvZCQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQALWvd+ -5ZNLJdwB6yA3y72pcTqvbXPZnjyLXG10RXZyAmR+5kG/KdfN+KcthzJtJT8UESuV -Wi6oi7qx+VJ5tFvq/rDusJwUU7pdZKq52coXspnaNBgxVoPZIY8gnmp/CUEvNvqr -59FsdlDUUWm5k66a64pvqZEhWKk9U+jBLG+IJWUDipCcjlhdmuJnjmr2ERkkjYm3 -EV6o3CE1epp4ipTCKYS7t6WOBHnc253Xp6O3OebDpb6DrVk67upKir1uccnkp0bV -o/2gsaNUjbwB+2hMWqL1eUT3uel725GRdFto9jqycO7mSfTxplNmE84vnohFZjSu -/A0UAm9qyay1P4m8 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDkb2dL59IbCuz4 -kq4e1OkAa0eDrU6e4sxSsIoERlcf9zI3zPDN7MC5uSe0GTOhIadMomzHVjHEak9f -+5JsIo/E6z/RKwbHe2uQgzfTWRzA2t6FodzmneHY/G/UwLRuNz3S105OBAlq+5/T -z7GA23p4l2XhvI1a+uyxtu4/yQODqwqajgMpiEIUUIARp9Uth8i9JTKeVfsi75xk -qKRiO9aGQxoioxxL7q8wcNOcqtq2h2F4hzIMC7dEFptEG05t85iZ+O2uQQJdUpqY -ScMkJAsYe79AzjdlDzIMHFpHS7A/2xe2iWiZPA5whJJcM8ttLmfIr0dBh703h4gA -ZR9+e9kJAgMBAAECggEBAKC87x+PQN18g6CpfdH+GPINiuXR9ieBCSsKRCOb50R7 -6Z8wGyWbeUV2TsTAkv7HsnQPOqHNOfmuoKm2WKK0cxuqOV6SexO0+cxXONoDs2LU -342ChvDTmY7YmkxHSO7g+iS5EcV9u67G3gDp/Unhpjzis3Ly/ThOpmyqftztMgbb -6KKfgGi3VL+fZ2x8gQt0II2QcO6GIzrPcn9ruEM6tXJhQ50YaSCeCmseKBvKuWPW -b/Gj7wgYhsiMW+nt8QjVam99eCQ6Q94CsapWRdGpj1Nrd4ISREbqr8x4fmcbiSO6 -6HZyUidxZIxr2Y4/BTd/BiIqXXdKAMoCVElmyE09P4ECgYEA9IkOkHq1jDLAmppd -fNSC/ndmITS0imvwzTTubXKOTn3yZHVxk2/ld83aauGA3UwAueW4Hh0hJdIWqC0d -IyaNj3EhcVbidyYwvXdChlPuQ1uccu/earhzsbaFwqNoLVgMrPcB5QcMKUs2/s4Q -tBXOqNlFjDZ+bkHQyGXtFYJzknECgYEA7yUcJqf2jB5e4LWG4Se4zD1E/ccZH3t8 -nhuXa6nDsx2trZBknHpQmc85WzbBITD+LRaamlYpnB1Ueiyr7/Efamtls7NvnoQN -14cNFnnj5HooTHJHgNBuL/M3hr/q8uK4lR6bu/DOfzUfRGowX3pj+POB01ObPdm8 -BUTFwmfJTBkCgYEAkakqccmGZxK8Q9t7oKX9uZJp1ZHNkT6m27WR6MP6HKtNPaXv -l4Fp0KlgV5Yn6qohLJq3x8hWPG8ea+MjnhKS9ETRRPAaShsHoXRuvhE0tg7V7GkR -tcRVtiAhIUWxAoGWW1lvWXuPNPHGupUIwhzTUyTJFrJHMWom8Zg1V0CzkyECgYEA -0JpPXwzejEUlv9+4owhyM34ygyg8KvEduBEbWWfBdKmryR2OFczAKBrRzlYJy3kg -DpaMD5qfOzV+bgAvjuKG496A3WrlL6HDLUD50qRKfQ9tvZll8+BcbWk8A0e/m1TX -bARCFoOsrNvaxWPXhEGPmSZYFc31OdOHJhViZ/z+Y2kCgYEAphx6cPXLMcgv0ivj -SgIG09vo3hGA5r06FkLtGL677CTvYsk/Equ5TkG0AIJ0acY9wiabk6zyM/9XjRAp -Nf8qYzhaMxJiyw+JEXmud4Dc41DqjGm9bLV4tKSR+7xzPBP9Q/QLxfRvArBOKhiX -L5fWmM5SQAoHsf124DTKckNirLI= ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-3-server.pem b/FDBLibTLS/testdata/test-3-server.pem deleted file mode 100644 index 27759468f94..00000000000 --- a/FDBLibTLS/testdata/test-3-server.pem +++ /dev/null @@ -1,101 +0,0 @@ -subject= -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIIDCDCCAfACAxAAATANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UEBhMCVVMxEzAR -BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK -QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU -TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwHhcNMTcwMTAxMDAwMDAw -WhcNMTcxMjMxMDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA43+uFNDYKXQQ4fmSencxdm/mfM6FAlGwRBWotptutznfH0N+ulp5RhjRcbGb -AHwPOBBNEBSIV3LhdPXep3NcmSkfaMdnPEgrurI0DLYbxZryEXdJZyoueT+w4TNx -I5mNlZDKD5bH5rhV/dUAmK/+LpWuxWraWYaHBZBsuqpb0MF6IZJAN9Ve9JiKHeiY -6ecz/o9XIrFFeWKMncHwBV1taPPoG2Ksjv8UlqqehrYXG+md958MXf69dkuQJLCS -rojPOkhUroixvGiXJBRSFCyVhQxPCLyASsEv8qPEKMUiW4oY3w5R9RQmw97AHlA1 -7xB4mGZTZEjUIOYDdd8LyuRpawIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAHMAsm -zLtFnDaYaOgJdPpi9VAUkZpbSXcA3a02PzOvLN9VV1Fogf1+F0zYFlWbiYGIwcI4 -3YfuFr97/e0uEQd6pwGc8/a63q+CunGz+HPStWZm+2ZgmJhBH6i1RwmhA9rH6rGK -j2UghYIYT83gn6S2XSfUwzV8gCw+JjJwczcjGpOf9dRCAEsRDcRwUX7rI16cE2tZ -SLzYB/Kg3wSnUXTKXRJfg6VbVRPFXHQlRYpOxe2z5LWoTEo2uYuHgYO+DzSO9pEj -WgyKBwcc+L3zIZFYCqc9EN//QrLlXsiwSDVMvtzVnzvIQKcGF7OE22NyojTaMzQL -2h8UA9W0Mew5PTSl ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIJALfRa36cuemYMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMScwJQYDVQQDDB5GREIgTGliVExTIFBsdWdpbiBU -ZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/J+sL -7POoXNdzYRsMZCI5juxMPOVue5vU2QPU9z/PHBsBICX6tVsCnkzk5CLdM6TxofgX -F+MqRKxtcIqxBcKjjVecJlqHuNY+jS2r8UjcoQm+EQ5RsBWu8yaSnXIiZTccQNjB -5T2awwt9Ptbn946MZfq6oEnn4ZPByu9/nCrlk7QXTkuGdpTnC6paQWt/lVxZfELM -i0g76/K/f3e2Lv9UCvlxKOwFMye9XjwF3ekEmUuio5JZEdn+LIs9zB1zehFhGlYB -TUXnkZ0LTOPbH9OxsOli04n31/n7UbYq1BSuoiXx5A2eHOunMppa0NDg7oXmDSKE -A1zo+QtIu1YPXaLdAgMBAAGjYzBhMB0GA1UdDgQWBBSeq0aNrc7mMaWHm8eCndN/ -w0I8qTAfBgNVHSMEGDAWgBSeq0aNrc7mMaWHm8eCndN/w0I8qTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIOw1jC39 -VW+1fqGT+n44Y6Oh66lAowIvi/BEOW1I4iPAjkr0g6FbbdKeq0VLL7aMMu+q/AOv -UETv7MdVh9xjTLqWZGN0R3Lr/n6ButI3E7MLKL5ByLNCoOhF42aBLINkpKSNFRrQ -40iNoHm3BaNRLKS7poCk5HFkEMjvxdQ1AenNbUa21DTh7y9arHF4CPfi8Ity29jW -ED8jYK/+bWIaO+YhGkRh8UuD3o5WnOti+9QK56qxkPtkqVTh9vMVHfD0DgVeLvMN -nZpTplLTfhjzyFJELwE/U+HJ6KIslmqwarJ1Sla+1gHCmJEbzbsrnb6bLtrHtXCZ -XvmR6B5iRkDVpw== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDjf64U0NgpdBDh -+ZJ6dzF2b+Z8zoUCUbBEFai2m263Od8fQ366WnlGGNFxsZsAfA84EE0QFIhXcuF0 -9d6nc1yZKR9ox2c8SCu6sjQMthvFmvIRd0lnKi55P7DhM3EjmY2VkMoPlsfmuFX9 -1QCYr/4ula7FatpZhocFkGy6qlvQwXohkkA31V70mIod6Jjp5zP+j1cisUV5Yoyd -wfAFXW1o8+gbYqyO/xSWqp6Gthcb6Z33nwxd/r12S5AksJKuiM86SFSuiLG8aJck -FFIULJWFDE8IvIBKwS/yo8QoxSJbihjfDlH1FCbD3sAeUDXvEHiYZlNkSNQg5gN1 -3wvK5GlrAgMBAAECggEAAVjMKwthfD1XrD7SAy+Zd14KO0rttqnNJVoLealJ6oPJ -MmIv6eKHPUcAVm/6vvH9FRPjoOi+NeZUN2ENRGiGb9GygehMUCsNNzm+3SRm3bCh -JkFSie3SAJ3D6lFnphJOqEgHKjh2ToNg3vPX4Q+JrbTtJ/YN/OGzAvFr81721DGO -L7Hs6foBHKrLeibbguVRdc5zc/WtWjGPFhNAmR9qincM3Q9DrUUHjbJzTS1UXDVT -zssTUTZe9TLd4buqHjLLfmiPoTV8qzv5l4RwkmuuLIT+5mO7X41glwdOkBfk+Cum -BZjrjgTDXbqLNXjMsvXkG1hCZQ6qwdT4GINYlYSiQQKBgQD4pJf7xLaX53rj+LDc -HY3TbWDdyS7h7cq4ZoKa1xPt4Va3xIAIst20edTr6tBYtNygjFZwIkPFYGwdGKVK -CqbpzTxVl8p5I3uoUmIFDo8hX7ChLC928K9lfD62agU85ZfP9Vly4zvDG2sIvxpw -HUY/96VhdSG3fssWYvg3dYUGCQKBgQDqOuyrcTHaZujFMN+MIuUExgYOMS0R0O4T -zCMtWIEkjntSk4CBMsHSb/dZH3CbFB90GjS+WklfTBd6kZ8tBO35vtM6nz5NPCEr -2umqJR5hijHV2tB98qV9qJttJrH/z0VKuuZBa14S1rJwGpX9ZoOULwcOGK3VC9pQ -YnH7Wdjw0wKBgFtBZXqE7xL/ZS4IVzjiK+xeJ4Ae13MaKB3XmbWknG7hFkep+ee3 -ZgFX+ZqAeukjsBnIh+zt1nu5cNSY+Akdsbb7mVo8tJYTPM5BNjJu7n8sNJJiuiTo -HyebGxUuAjAgf8BWZvbwiT2JcZYrNVPSmrbdeDg1miNTiMv1lO4d1q2pAoGBANq8 -oFwSX24IAIR1+a2SwLDOhMUoI2Cp7ktKrecg6alL7drVqIH+9oYgzarK84u/JQh1 -mJ/TDQYTtzFdYHrYSaybCgOKxtG1v3yG+QNNmquYNKXzrBSSTv2kQVGTe1LbK2h4 -VaLuM3IAUa7jBQMZgvMVX89IOL3mTcAXzz3dT/zFAoGBAI/pVbABfPihWZ1MrmTN -pnRmQ0461J0WGT+fIgAPR+R+umckHaOVAGiSQomfNrUBbsydoZYu/by7GhIGsDeO -8XKwEP/HLRrABvZu4KLTxa+qTnW/t6BSIfFwQmrNMofxcFRbdzNAODKjyaJG2dqT -ksg9s2SxReRrGOeb43CAw5SC ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-4-client.pem b/FDBLibTLS/testdata/test-4-client.pem deleted file mode 100644 index 87f088b96b1..00000000000 --- a/FDBLibTLS/testdata/test-4-client.pem +++ /dev/null @@ -1,80 +0,0 @@ -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Client 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIJALOPTrQGpeslMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGIMQswCQYDVQQGEwJVUzETMBEG -A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB -cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM -UyBQbHVnaW4gVGVzdCBDbGllbnQgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALVkdxOmWcd959NyirJ1iz7q5fkjdRUV+88KMMehQWc3f50GJIQ+eZo+ -7RhwVE+n8nd0i5iGfyY6LRuupdwoQUxoZ/5rUIDGKspNO62DVRW+tZqzpEa1+ub5 -75BMoc7I7l9sXDkuiMu1OYcPNKMv4F3mf+B3ourLqjUekKlUv8XIZXAvN+R19HlR -FM8vs8rnhQXx7iWVP91frDvyD8G7lOf6R7R4homnB37kLom8WU+fCmcyA6em0qX0 -JeVP6xk2qXU1cMs7DL8WftdrWHv+a73/l4hytQHo5OvtGaLZhpPYpC/FMSaFHVSM -irWSFK+ZtvaLi3LXc2HGANMokjPoRf8CAwEAAaNgMF4wHQYDVR0OBBYEFPtTL9KZ -jn49cLediy1ixz7AXOI3MB8GA1UdIwQYMBaAFCXTF7f83Hd7xm9gR+O4QrvjNo8Q -MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB -AQA17a4d/tSWIlTkIfkrXziD21+1OsN6/dUrWQK7kxtEe21QXIutccW4bwpM0JDB -M+bZiWkdgQ15+ZotX5UXlBcx9WWDU5RqSO06hhXu5b8gZwfVF4Od6tBdVxkn4KbU -0YujOZrL8fDOrQHqCO7nhNlYgcEn7bKF5wjtOoiKhtA9sLSIZQR5g32kkJXXGvcY -lLWMXygEg9FMQoldW9RHq4GbUiYEeqEq6k4S7cE03R1lvmQEOOAJ2S7LnaS4UHQT -GmW6uvLnJJrG4HB9JGE+y1e9M+C7Enzhi39RGd8ylignGimkdw/1UEWnvKGCqoU7 -ufWGF7eUV8dCqO+jYghIY8rA ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1ZHcTplnHfefT -coqydYs+6uX5I3UVFfvPCjDHoUFnN3+dBiSEPnmaPu0YcFRPp/J3dIuYhn8mOi0b -rqXcKEFMaGf+a1CAxirKTTutg1UVvrWas6RGtfrm+e+QTKHOyO5fbFw5LojLtTmH -DzSjL+Bd5n/gd6Lqy6o1HpCpVL/FyGVwLzfkdfR5URTPL7PK54UF8e4llT/dX6w7 -8g/Bu5Tn+ke0eIaJpwd+5C6JvFlPnwpnMgOnptKl9CXlT+sZNql1NXDLOwy/Fn7X -a1h7/mu9/5eIcrUB6OTr7Rmi2YaT2KQvxTEmhR1UjIq1khSvmbb2i4ty13NhxgDT -KJIz6EX/AgMBAAECggEAEm2Mc2CZCl1OKfsfABZU+SVgC7mAcY30MQp1/jHxtQy8 -WDWBjDXUoMj3yV3QEu+bAGvEqtAvJrEOWBucGgu05pBM0FoSqaJ4QmkqQOxwvm7L -gFXzwINIZCLMJbrDTYC4RtV5YQ3LM/bLS19OF64Lez6piyJcWMIsHo1mYO2NNgiD -7f1x1uQw46Q0YHWeoHY58MPfmgfKsqnJDWc8cCuU9fJOWeU4dVrfW8dh9WVAoLZ7 -qAM5vvap11Qk8RXaRnmLjxN6H1M7iVNfcLVNKfG6XOBBepYjZr/qMkuN3ONuqBHl -fC3Zia2zQZRfiuPspX0KhjCfYAKbIZC6oyrQM2uXgQKBgQDoD5voZiCOeGXJEMUk -9JV4V8A96aE0xxy+OHMogVpysxBO4V0Nh0krSLTt9NXnpjawZQ+3pLQ4+2J2XM2e -fJuJJ7Z+Mhjv6epnMM7FoxK1VF7oe+LE7Yk/kg/moCuVS/XhLdQrhZVBJhfEADS3 -oFybf7Q6rJYtN3OYsiFymyneHwKBgQDIGsY5kGdmx27LS5rPMwdw632TF8G5BGbu -C3ty7LYkOWb/9/V4cuWjW4eLJQqCWbJQrzOvg0coxwXLUuEQik+IP2IkF0YlRS43 -VJuULwOxi3Cbj51RoapHhmYTO9fe2A1N9oJMAqEUHY1q/r9txPcguRWyuH2Yv9Ih -OzHnc2DcIQKBgQCGW0MxMq/2zM5hs0vxMYq4ulWbgwDKxd1mZNiHwxzS+8mdYe22 -P3WlkdrvSqnuDNXtGxYWhU2zEBjZ3rFN6WdD6bJHLkox3YTRafjNhLT4N3kbsV6C -FeU44SBDrsiNEAWz8gy9hgH8TknEOTpMdpQnk7CNqA7q7wgGiFvFNwDukQKBgG7i -R03Gs0XE5aRJtPN0N39fPyqvU24O/mqSekno2dWg6W6WHLQuFwo6whVc5UHuKl2D -eISdnmT+RDuzJXxg6El7tgqByyEEAOQwQjYPB2Du/+tz3Z1KlG0mEJI/6xNVbany -G6m7Gz9mUOMlXzaYmsjLRzbN/OsUAIDhqHm0+cuBAoGAZCND80akS3xr3yC87GyX -aA0RoHXbdB6dbP8Y6XYDXR4QFIA4kXwY5cCLaZA/0hP5FOzDhORmaoaPM8vUdNyb -IYvbw2H6tODiU5oICWY6+HQQ2nXikucI4HDYDLbsiV2htZkEmBYWLilYq0Tb8jC5 -u+ehIIvZYLqKaY1GaKmF86A= ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-4-server.pem b/FDBLibTLS/testdata/test-4-server.pem deleted file mode 100644 index e80346e9b29..00000000000 --- a/FDBLibTLS/testdata/test-4-server.pem +++ /dev/null @@ -1,80 +0,0 @@ -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Server 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIJALOPTrQGpesjMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGIMQswCQYDVQQGEwJVUzETMBEG -A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB -cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM -UyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAJp26QAmlMusO7C8Py/I117r3kHvB+My5kIrj8g9sKpktwTzmsJGpvJU -EaKISEdBsJHLGnZJhwIhr/+MG4WDEM4oFNCtBQZznV3wjIQWq1w4IO8/f3+nBPpW -f14fjs1E911Uo/ZOL9bxvh1SIHkS6itgJi+tgVPx7C3s3W3mC5nU3omsE+Rx4DDm -KUq1kyN1ELBIAceQ4wTmQ5B8dv6MSW7zt8Jdrhfhg2GJIPPB6XUZJ2yIOvgu55GW -J5sMPa0uNDfCsWJ37fzFm+XJ/D96t7x8I49IyfzbIgcU9JYFlcqkryvKh5IpQGGm -H/I6adIWa5xWpMhB2PA6kgtDD07Hu2sCAwEAAaNgMF4wHQYDVR0OBBYEFJ7S+FUz -9ngzH/TNPVeM/cE7LeBGMB8GA1UdIwQYMBaAFCXTF7f83Hd7xm9gR+O4QrvjNo8Q -MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB -AQAx6WHwikVFAH0TRYCznwO6He+0t2pnlyfrI+24N28tzupMSrRPs086UbLgHLz1 -lbkYdheeOkLPzjWi5vfymL1Oua3E2iAXWEpMb4Sg7E5SVHp9yt6gZ0DTVwR+Gcu7 -uooroidAG3OFeOXL5ivU5J5ipaoEAiLprpKxtPzo4z/TxIqw3kJISC56qw9VTJNQ -TQZvneUecykdIZuH61ih0cJLe5WRkEs/63Dgl8TBYiVDbvBSGRbsXoAXcspVlc2x -XOLey5IVJ4/TH5ZBobShC6J1KrjZTNYvUgc44CocOgrc0ePPiQzB7JXxR1H8ATGl -yKjWqT2PkrfHmjdcmsi2GIVt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpeshMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAyOwdOcYcH3wYou13CchsXh3lLWA85E550tT6/WwDnslQjiMZHFrKvUT2 -B8CUOR3Fr+4RG+cdw80rgojYEUuHKwmIGyjo5IotdaYbWzf6mvYThlIPPudCCkSU -CTtqPv8Oq4QdIpCxHdix0MINKu7c+qt1rUwnDFQSv/gHhVnNxT4r8pwVp6T4hwka -2YQaRNjzUuuFinMub0UtxnUX0rH8X5STlOSVn4Ksjo0OhQzsGEYDx86jVAXjgGcb -2CgGGctgq04hVrngP5ahT1Xeh9YycMlQJXsckJJBxfUJebIjANSRyzxI5fYt+ZkY -qoG5VLPREUQknxcpbT7Rsj0n+k0RhwIDAQABo2MwYTAdBgNVHQ4EFgQUJdMXt/zc -d3vGb2BH47hCu+M2jxAwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAGwW7bRqB7aYUGsD1MOE9d5slp6Iw6wKyNLdg+mzoV+iCV2ZM7ejNRButiAy -vPOxSQwXcibLm/g599e+LY1TiI1XXPbL2bFnTcnThqpHHFe+eRrDgqxO8qJyrcBp -EfyMCJWq7jFg4bWoYTpLeC/RAKyi9fxlqY1NzQCp1bG3LiaDJ5VJd4uwkgX2a0yN -3e0XEFNi7r4u4IHejwFjKWrDg8sstjbY+XOYC4EVQyUsbzeKZKSqnOdR2Jv1QZHH -5O24G/efIFpsA6MVUOfRk0eq0RfKX7CdHn2a5p8aC6E6YMDhXL6xo146n49t9sYD -HMUnfG6AEboTBa/l+zwCG/u4f/Y= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCadukAJpTLrDuw -vD8vyNde695B7wfjMuZCK4/IPbCqZLcE85rCRqbyVBGiiEhHQbCRyxp2SYcCIa// -jBuFgxDOKBTQrQUGc51d8IyEFqtcOCDvP39/pwT6Vn9eH47NRPddVKP2Ti/W8b4d -UiB5EuorYCYvrYFT8ewt7N1t5guZ1N6JrBPkceAw5ilKtZMjdRCwSAHHkOME5kOQ -fHb+jElu87fCXa4X4YNhiSDzwel1GSdsiDr4LueRliebDD2tLjQ3wrFid+38xZvl -yfw/ere8fCOPSMn82yIHFPSWBZXKpK8ryoeSKUBhph/yOmnSFmucVqTIQdjwOpIL -Qw9Ox7trAgMBAAECggEAOZAMvsCh/NDfobpVddJL6JTPzBRvBQ1H3+rp9z5+ItHL -nq3Fw5aeynnn5IETJnLlgT+GSgSWqoWxV/N3oia40YsATs/bqo7VW1e0ldj43TIR -m/c25XRxl3U6m/H4vqhv4rkTLUvv6hNGvRiI/3W8DJQVRvlK0+S5FlhKIJV1R0sH -tp5vmaPp09Ln+NVno3u3iaYkVgVME4Ukul2i03sQ9OgvZSBCaVr//fMpiPdBeeN6 -QY6XHjeGQRnP/UdzMYJ4Qz1yovL1ntneaTMdz/GkKuAFoNNh8Vr2kiEskW17OWPB -ZGcIT6YpBEPo34xXUhUQt7ylFPxGH+zZyHZ3vb8j6QKBgQDJPeu/iPg+M5nz5gO5 -ge9gzYrhxK/1mwbFlD7qt1NjOSm6xWxUcss3STjuG7jB0c+NopIUoq/egsUnxrRm -4l17uOCYNLbhTJ2ynfv6QnUMxW5Xkve3DkLa2bze/fhMUywTy8N4A7z0+y35qzm3 -lY4rLmQOQKPkmqWRnxU1u8fjFQKBgQDEfpOZ0fp2D/1gTG+D+/zrMEbjnNn3ZO8I -wrjoXwRxcRggt7lJhxgQpwtDr98IqYkDzX7bvyMFJuyTii3NM6NYycpA1pHX70B/ -xMvOcrgJnIUAoJ7nl43Or7s8bFTPDLaD9PNGHjrlkF3JOXqSKEbw367jHVOa4SYr -OjrogjrEfwKBgQDHU2a7ax5+9btqggx0ZQfGOTBzmM60lZ3qe4CqGXUl1YvIrB01 -tBImq4cRCTJB/9/1qO3KNK2/1oUTddRgB5ySnDcRaz0tASc9sQ/Q/JxVTwSRB0gG -78A2Zu6VbLbQWp1Q6kWtDP7PJC+QmRFtDlwn1yZRm6L6HlcaWpi2hU1iVQKBgCEu -ashv8Aad3qCzZ6V3GReyOFZZd2lSjxcAou8ClKJ/gZ6Mx+pFuOee/cT5XwV8c5nD -yuda2JQXJZ4omGFtlej5coEOeuRnD5JD7lK3hqKA3ujjNtJPAnBjto+Wj5/DOtL/ -u1Ec6782aNABN9SUnp4wd7z8h9DAsoxcMfRvgXMLAoGBAJ9gGttfqZbuPz9V0rAo -p05SPPado1i5+2dUOScIbNB6+vQij9IlR2Tzu1T9DwzrBqTDPPmSggeA/JXeTvh6 -Skb9fDukizeDfwPYUN2gljhiJEqFdpRBr5vP0lFi291+a0jMW1zldrumxCcGKMyU -D5ReKLp/zSQSQi/Wt4FF1II7 ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-5-client.pem b/FDBLibTLS/testdata/test-5-client.pem deleted file mode 100644 index a7b8ef6235f..00000000000 --- a/FDBLibTLS/testdata/test-5-client.pem +++ /dev/null @@ -1,80 +0,0 @@ -subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Client 2, \C2\80 <\01+\02=\03>" -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIJALOPTrQGpesmMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGdMQswCQYDVQQGEwJBVTEYMBYG -A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM -EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr -RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBDbGllbnQgMiwgwoAgPAErAj0DPjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALanLExQi/yK2PRyu6Mvdw2MRxUs -26kJftYuK7KtYyltTO3vtj4kNKg1vZI1eezhqr2Ta/1DzE76eLVs8EOW0LAb5oWM -zXdYBXBX4vG+K7pYfjuvZUd6jfX2bHW10xC96HgDTfRn6dof8GR0fILJ6DoEcyI3 -82xnKKxTsgAuXU4uvcsl0g0F78nXuIbk8ZktTV3LIdbOCIcLQfG7DdDyAfEA0T7Q -Vg6eeLknIUvPePxyWkUdYeSCDP2d+3NIlHMxNPmH1q3+fCsEsy/kqdVO9e6KrZla -CKqnc6yYTXvTffpPepC3Igz678iGg3dv9rLj0i4fyTr4tEOTJebO9Ka3TbMCAwEA -AaNgMF4wHQYDVR0OBBYEFKO2/D1IhG8KWFwR6OdyoFqEzIWAMB8GA1UdIwQYMBaA -FJFP+HFpDrD0BRU0yE606s6xkqFBMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD -AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQAQV3FjsvZvwi5Oi/oSc7Du/BQS9nQ/D4j6 -IeYpd3M0y50awZB83BReYrhdC907xKkLRD0R8oEPDEg5SaSj3vRML4kaUUqnEINW -4JQtv4wNO9CagYriGg8ygQa0xd683svHeXDet3ov11XN/Ms8lfDiOUp2291HgeTW -8hqn1DaNfZrCb3EkdoNThwVKIUzQtEPBuPkLE+XT8kZP5d8KHmv8/9L39NdZY32d -fzKGBeCxZ34pQS0cTap3rZ02nDfV2vNevODRyuqdhs7EQps2Oe1IfPB9GSE0OFUQ -tdphxSjsv1BcHpTwBDpIITKarnceMIKxQjcZU3yPv5ibIaGCgZOt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpesiMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEApTSBCiUb0amf+QRV2WY6b3bK93D/PSrm4KR/2m2V0lciU1DAk00/kZ52 -ZIZmq8g9EaE2+CaDtU0fMvDZpaZD+vTFRwsx4varehq0ZwX9Wt25i/3G/eGLNlD3 -9E4tDNruK5UQjum4nJ0SV+AdFEGkSfeU3ZJEHYH0NrcbyAUbh0KeWCSwHiYiFJJf -gBYwRq/HdKNoS/4YvLXzTLR7BSm3YcqWlO5tdkJ2lcT/7Th/Hq1TCW/FKwdQJJBq -JrbOYGlMrf1pLO7Drei/xhsYkwTQ899MhSjkBRhc+401p41Mky0n8wLkuPJGhoY3 -9QUOjT+Rmvq5yryg0eWGiFquk6Ru5QIDAQABo2MwYTAdBgNVHQ4EFgQUkU/4cWkO -sPQFFTTITrTqzrGSoUEwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAJfCHU7dm2/2ASyt3wyxivQLxlV6FsEZcF7HcpbbxuB73frGOL4kEoOxvr2X -fBGyjlPMotbc1MeAalAv+hVHdcAcBFPF7lxtYiV6D7YI5T5yVbWSASG3+DMAiW6S -GdQi2eyeh00nH7Y1IkW+yaky0enBtWLzrw+XzHl6xT6DIEJnir//PNxvgXTJ5sjk -6eFAm8HJIqkNQmgfChMQfUH6nm66WwULW6I117RCSkXhIgxZ7wzDq8bXcEdXCrZk -yy5ket9OiVpbd38JgdYirBLmCQVq0uDOOPLz4ZJmNCzQzEt+38AAK2azAk/eb8W9 -JaKWH+5V8lhlyGw1zQKdNEP/wg8= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2pyxMUIv8itj0 -crujL3cNjEcVLNupCX7WLiuyrWMpbUzt77Y+JDSoNb2SNXns4aq9k2v9Q8xO+ni1 -bPBDltCwG+aFjM13WAVwV+Lxviu6WH47r2VHeo319mx1tdMQveh4A030Z+naH/Bk -dHyCyeg6BHMiN/NsZyisU7IALl1OLr3LJdINBe/J17iG5PGZLU1dyyHWzgiHC0Hx -uw3Q8gHxANE+0FYOnni5JyFLz3j8clpFHWHkggz9nftzSJRzMTT5h9at/nwrBLMv -5KnVTvXuiq2ZWgiqp3OsmE170336T3qQtyIM+u/IhoN3b/ay49IuH8k6+LRDkyXm -zvSmt02zAgMBAAECggEAU2sYHSZwOH+FRGcd8RJdcg+N60rYa2QNzG27wVfUwPfN -OaHP/qN0dRpOIPdRXvFVlE0+9aVAKxXTiTBers+zMascZgP/VrEZksxgtn1e5TVD -OakKPVHogdvwfvXylmPVRvJjaOsIb3lExew5bVYfPFgJ6Sfagbi/Z6y1z8VdEbYb -mI34KSZA4bBAMAHPZLa9TGEx/vbPsBlqpU6k8lcoy3cTkO5fCZW4ZZIpwBwef4uJ -UozhRgtTtRBiUpk0F9IoOXonZY1Dtpg+HcDMti/FYgahBVe1hadJ+lbVTxH6GxyI -NJYvptdq5S99UOoJDmCCih0v0ZCUNYWoO0I0vzNncQKBgQDemN7es2fIBstiPjOf -p103DF5j9Uxq5YH9B3wli0CXf6Z2w5uosONoJWgJZKsHJ6f+YSuHsoE/eCrFF3U9 -lxT9Nie/wYYIGedly/VR143aCdiTXI44m5gxXgwaUcjvY1DpWyEAAmr5XNdoyZ5n -LNTvOTb4vVo9SgDU7II7rdpRmwKBgQDSD9aBtIy/650suQK/9RiXRU0Kg7LXXVM5 -lavPgLvH55lufJeGSa8+ofCNeo31N4AaVuU4lkGeny9tLNBQbYAoyAz0lf51qK7B -1u5JqBDyRrIpdkqwbT0FT1pu1LA3+Qg0KQBrTCnOx+YyyVSivR4YMZzJjmwZGKMg -BWOi0PzhyQKBgGR44dfpaIWbs39zjf+ZHnTza0N4+/YgA60/DKUxloULRArFPeRF -e0+N2siqnJvNJYGnQGuugbIxPjTZ4rxbDklAgW6HHkVX099Z0TAQuGFbIltZYoRg -jrBxv8q9cZHD5Uh/LoT/kmNdqYkNwCbX0IDt9UcOyMVzOq7g1eO0FB/TAoGBAMaG -tWIsMwGHOip0SAcHKtB8bI1NXo5v4yH/NDuOHOqXFcj383S02uzEu8XaV6Ozalx6 -V3SdfTLem0IBIneApajlOGlIAQ9N9qu358ixECMJcYQCCiCnfQ4xqvQoCss7judN -ANpnRvPotMS2xkhvl6uh594NvlgRksnGjh3oibcRAoGBAJKiu5ajmIkelzAhFMEC -Slxhg/E+djJ1/SG/FaF8zIyTOxre/QUvmTwFKtHe6A5EfKQo9GCTuHuAcJ1U7eQP -l2BoY0POqJFpw3s/QOt4g/pOz0YjD9GD6awL5WDfO++s4mnI1Snc3wcu99N4Klax -htsaEUECJBUF0ZpIFad73s2f ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-5-server.pem b/FDBLibTLS/testdata/test-5-server.pem deleted file mode 100644 index 70036359053..00000000000 --- a/FDBLibTLS/testdata/test-5-server.pem +++ /dev/null @@ -1,80 +0,0 @@ -subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Server 2, \C2\80 <\01+\02=\03>" -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 -issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA ------BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIJALOPTrQGpeskMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE -AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x -ODA0MDcxNDE2MDJaFw0yODA0MDQxNDE2MDJaMIGdMQswCQYDVQQGEwJBVTEYMBYG -A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM -EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr -RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMiwgwoAgPAErAj0DPjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALAolQZIGMeL5w/Bu2X6lHWjO58u -+HUDtBmr37So4jazhZBSFDBg+QlRMiYGLev9EhvCrUsVcRwtvtcuMI3wfKl7qgbi -ZX8zmrzZ3YJo9U47NzCa05faOl8uSBvuXuXUBLU342WFP8XDB1W8yOBQMK73xoFv -DkcxURx9ZtOhdC3EgYKrFqOB1Azl1DB4gLV3h9rHW5QpQ8SqD9CyggcDBpDeZQIP -+4l5YFE9Nb4kEUTscz2wGn4TdHMmcnVpfUxp1Y2o8Umvh4llXHIPhximGb3JJ4QQ -Sir4ZXeeoooWoJG0sdlqVLroKav/VMGtEu9LyfbrNdKnTJq3ceVQ+HJ2hlMCAwEA -AaNgMF4wHQYDVR0OBBYEFH61Z8O9vFsVdhM4MBU3poX2UMTEMB8GA1UdIwQYMBaA -FJFP+HFpDrD0BRU0yE606s6xkqFBMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD -AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQCVbxlLGIBCo6/XXjqoMyZc7uQZJj7pGnwh -nIMs2izCLfax8j+QrThO2Qjn03zT/WF8eG6ibPbjgnw3VFwCkV6oQ+BXG6Yt0xqP -4rz1LzxSio6HSm26gSk4SQUsVoAtz3OImoTCFVfz+Mixe87pyVXXEEtCYvfU74H9 -I1WGyNkWAxiJbqeIxF5PKoc3EdnT5mfdC6sdeGm7t2neeS8PDFQtJ4UfVIEK5z1C -MOfQILNkLX2nBYxNqKpV66zf68VZNN9002ZH2FITGqImpj74BEws3sheiuZySdoI -wnAwRnymIMfAmkf9C7Q2ugId0YMMyesaWrIwSlXlJOHGsA1VrBRD ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIJALOPTrQGpesiMA0GCSqGSIb3DQEBBQUAMIGHMQswCQYD -VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v -MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEnMCUGA1UE -AwweRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBMB4XDTE4MDQwNzE0MTYw -MVoXDTI4MDQwNDE0MTYwMVowgZExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp -Zm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xEzARBgNVBAoMCkFwcGxlIEluYy4x -ETAPBgNVBAsMCEZEQiBUZWFtMTEwLwYDVQQDDChGREIgTGliVExTIFBsdWdpbiBU -ZXN0IEludGVybWVkaWF0ZSBDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEApTSBCiUb0amf+QRV2WY6b3bK93D/PSrm4KR/2m2V0lciU1DAk00/kZ52 -ZIZmq8g9EaE2+CaDtU0fMvDZpaZD+vTFRwsx4varehq0ZwX9Wt25i/3G/eGLNlD3 -9E4tDNruK5UQjum4nJ0SV+AdFEGkSfeU3ZJEHYH0NrcbyAUbh0KeWCSwHiYiFJJf -gBYwRq/HdKNoS/4YvLXzTLR7BSm3YcqWlO5tdkJ2lcT/7Th/Hq1TCW/FKwdQJJBq -JrbOYGlMrf1pLO7Drei/xhsYkwTQ899MhSjkBRhc+401p41Mky0n8wLkuPJGhoY3 -9QUOjT+Rmvq5yryg0eWGiFquk6Ru5QIDAQABo2MwYTAdBgNVHQ4EFgQUkU/4cWkO -sPQFFTTITrTqzrGSoUEwHwYDVR0jBBgwFoAUnqtGja3O5jGlh5vHgp3Tf8NCPKkw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD -ggEBAJfCHU7dm2/2ASyt3wyxivQLxlV6FsEZcF7HcpbbxuB73frGOL4kEoOxvr2X -fBGyjlPMotbc1MeAalAv+hVHdcAcBFPF7lxtYiV6D7YI5T5yVbWSASG3+DMAiW6S -GdQi2eyeh00nH7Y1IkW+yaky0enBtWLzrw+XzHl6xT6DIEJnir//PNxvgXTJ5sjk -6eFAm8HJIqkNQmgfChMQfUH6nm66WwULW6I117RCSkXhIgxZ7wzDq8bXcEdXCrZk -yy5ket9OiVpbd38JgdYirBLmCQVq0uDOOPLz4ZJmNCzQzEt+38AAK2azAk/eb8W9 -JaKWH+5V8lhlyGw1zQKdNEP/wg8= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwKJUGSBjHi+cP -wbtl+pR1ozufLvh1A7QZq9+0qOI2s4WQUhQwYPkJUTImBi3r/RIbwq1LFXEcLb7X -LjCN8Hype6oG4mV/M5q82d2CaPVOOzcwmtOX2jpfLkgb7l7l1AS1N+NlhT/FwwdV -vMjgUDCu98aBbw5HMVEcfWbToXQtxIGCqxajgdQM5dQweIC1d4fax1uUKUPEqg/Q -soIHAwaQ3mUCD/uJeWBRPTW+JBFE7HM9sBp+E3RzJnJ1aX1MadWNqPFJr4eJZVxy -D4cYphm9ySeEEEoq+GV3nqKKFqCRtLHZalS66Cmr/1TBrRLvS8n26zXSp0yat3Hl -UPhydoZTAgMBAAECggEAVD60NlLYduXzVNfDtVuHEFNGOjSOYfepc/V8gLubo6lr -IMAAI7rcnpYUM5cU8x0OQfRyR8wzUdSWxfWzBs6R78PSZoRzIcgeIl7Wzn0/g3BS -To5czuxwqgBKQAFZpPQmZDwcJfr5qqxAn8IvFweCoMqiRlhELcvqDIP0XxWBqDjc -TNJ988XzZXQmJbjjpWOkUBy2Uqz8lZt8MmxKFpW7SW4tBJwPphnorgjWfjCV/VEh -ORio0rG74NHFo4f1TSrdU2BcB2cbVJ4B+bcUYRdvYmS5bmokhGF8vir0l43gUEdz -Fyk6MaPrTI6cinqzenm3q/0eRvNhBE56U0tiGLn14QKBgQDkCkt1Y4LEboSwsVYl -IXriStqj9p9MOizihh0enhzRXTTQuLX82fNi+bh1LAluwv290Q57pvKa+hB/YciB -o4s7QfSojxQY9DxqvXN7CvxPWXHTyFY5sL4Rm807+C/a9rd39MxBynz9u/7YRvsA -s8v8Y/01qIHnTo+mpDvu6HttWwKBgQDFwdRkgstuE+dXZZe8g1ivh3RNPa968TE3 -b8rzF9/nOJV7f6B/n6YEmHD/cHF5mm1bR+zt/jtf1NCRMpazchw3vT3JzQZYMDnM -SD6vxTs5rG47QLiNyTIRmmD4gsEWBpyvoyP8E/9QdfDT1bWI5zZnky9CquRlN+cu -J1bTsefEaQKBgGJsRxFNd91MThztDV9NSfptkFyAT1TZLxI+DEdwusNqVSdY8cNG -VpP7cC+yaAfURSwuFPAtqDxXfdNc4uuBKNDUsMInrubuUz1Gs5cBsNCWrFhZ+U1B -CWgUNMqTXiRFo/40PAyRVs003NOAH0m4UGyIw3rrVdX9xGaKMAv3b35NAoGATkkl -I4UDs1f9xQNaxi3Y9ePRjqJUzX6d1SxUU1eoM4ia5IDpsJwqxLb0RKrmwRT5JaGb -kbuLFazRxCkar38E3Kv1weWAFXlB6DTRXBPgFjzEhoBgjwCO6ZkLulVIysdjT8Rt -gmUINXn7FGENtFyTlP0XQHUWZVt0ETlRjgxni8ECgYBYv6MoSr0iPjQpxeKvwFDz -d9zE+ZXN+3GwtkI340lKRSc/f0Uq1TlC2w+DzjyyXcrBwubMQKTKcQQSH9f3YbMu -DuxVE9AXdlQ1gSQHGjS0qUWwsS/8Xcjk8ZuduAXPGr/MsvsW+FbbZqG8qdZTeMHu -MSTpOxu9HXC8SHML+y0cpw== ------END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-ca-1.pem b/FDBLibTLS/testdata/test-ca-1.pem new file mode 100644 index 00000000000..e833d4e19a5 --- /dev/null +++ b/FDBLibTLS/testdata/test-ca-1.pem @@ -0,0 +1,26 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIID+jCCAuKgAwIBAgIJAJkdwMd9MHxpMA0GCSqGSIb3DQEBCwUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBiTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xKTAnBgNVBAMMIEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +vfuzumqT8vHWNOwLPnXLJitIJ7vH9leTcWq05/PfeUUyaPDbt1u9ZRFyexew+q3A +DUNKKOrbo53B+aVipxmqmfo8w6jWzv90MpnCOTEQOHeJyiBTUG99yafy9PCE2KDg +T0dXayOeKPjAoTrHDYkZmd2lA7+V+upz7R7bNDjotLW/BctVllc5fw+ky1hWAKqj +Buda0ZNElCI78U9f2aMK41/J5vLaQzQvZ68m5cgKUQ6TwWZ0ZX4p9w5aqiLRlvK+ +Wo+lYe0IFXShX73QIAlcFo+apOZG652VmMXk7vzo8llbxC6s93bwm/WxZ7K3St3O +ttcIiE6mIoG94V0Xzby/fQIDAQABo2MwYTAdBgNVHQ4EFgQUCFkAB0WIBjGP84hL +whmqKiBbyXgwHwYDVR0jBBgwFoAUCFkAB0WIBjGP84hLwhmqKiBbyXgwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABLO +dQJuW85pcOhvMf1eaMfNGaKraX/r/+a0TsbbbSDqOKhytqk7JQkGEEzyBuJhfRmG +/jXkikWAId4SBBR67FVcwyqnegzY4ObFeH22hAjfGqL1HDDu+bcpPIJzGN1CqOXB +DVjAo1tKILP2fAVHyFNGKCk60zECtXv7QfkqjMu29kMgkK0dofGIAQeYpJP1WBtH +DUTvzdjv6nBBYMLtJV1TexcSMPn4fAKDfD6uWYSeGhP6J0mfYNHOE2Sn0MuM3+yE +vrPLiCmNctnnWfcIWiUOXA9BF84MWwglK0UETR1CEONvvdpqlmvGf3G8S1r8P0MF +B8mQBTsexWGMb2NTeaY= +-----END CERTIFICATE----- diff --git a/FDBLibTLS/testdata/test-ca-2.pem b/FDBLibTLS/testdata/test-ca-2.pem new file mode 100644 index 00000000000..3efad2691d4 --- /dev/null +++ b/FDBLibTLS/testdata/test-ca-2.pem @@ -0,0 +1,26 @@ +subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +issuer= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgIJAOikTZQKEq0VMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYD +VQQGEwJBVTEYMBYGA1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRu +ZXkxGjAYBgNVBAoMEUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVh +bTEpMCcGA1UEAwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDIwHhcN +MTgwNDI2MTYzNTU0WhcNMjgwNDIzMTYzNTU0WjCBkjELMAkGA1UEBhMCQVUxGDAW +BgNVBAgMD05ldyBTb3V0aCBXYWxlczEPMA0GA1UEBwwGU3lkbmV5MRowGAYDVQQK +DBFBcHBsZSBQdHkgTGltaXRlZDERMA8GA1UECwwIRkRCIFRlYW0xKTAnBgNVBAMM +IEZEQiBMaWJUTFMgUGx1Z2luIFRlc3QgUm9vdCBDQSAyMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAlZBDX6/eZDsu8JrEE3rgBcq8qRSAAhcI3B4PRz8K +NgwRGGq+8BrjsTRMqFdl8GpUDbzFKzpZ3I849GwkK+S0YU86dAQa0aORL5oEBisM +wjxDSRe3qyFZBnyJJkVo9BqF3wJhGCnWYNtFQ5OWnBKS3ZPf9jUGGL1q8ZFMoK/T +z5o0KmjH2VIAIW/Jz8Ouf3XQUHG3qC1C6l//BPqKWhxE0dYcDKBTSNMeI/NOsJ2X +PJn4g4NG+SYurQwTFYuFUTIig4p7Zu68VvgyNK/sgWoiR47AyyC9Lc5/Ojr0GMUp +zOhPEUba7ZnSgSlQLFdOyuKPO9LnaRLZ2C0uaW7ZHcXVMwIDAQABo2MwYTAdBgNV +HQ4EFgQU/DxpqnpxElyiZZHTJBmO7bTajGcwHwYDVR0jBBgwFoAU/DxpqnpxElyi +ZZHTJBmO7bTajGcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAE7VijzPBR7ouPkAQI6OEe8iWiaNE2OziZhOofai84ky +ExhGrd5NAC7eSLiIe/iXL1T0XobMwxh+hglz9zqCQ8xOLtH2z3xH97KSehnb/psM +Zz/7fOyHlj3MorRl9xpi7ncz9IY60hKAeYQ8JxSz5Wp/9KyXP+JPGCcnDal7AphW +adesPBGh6eGeAY0EDstETBsm1uMg99ABWeJk/moOlK/8F05lHUa15Pkyxx//S/cf +tKJUUcj8COm8vXvEFIBPi2FH8DKZMLFG8Ld/9N+lfDgp0r1yERxdWK4aB22iRoTL +LX97Vky4ozfdldIFBi2tzYggraPlgVn+Rxza6XXc2xA= +-----END CERTIFICATE----- diff --git a/FDBLibTLS/testdata/test-ca-all.pem b/FDBLibTLS/testdata/test-ca-all.pem new file mode 100644 index 00000000000..8300ccccd91 --- /dev/null +++ b/FDBLibTLS/testdata/test-ca-all.pem @@ -0,0 +1,52 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIID+jCCAuKgAwIBAgIJAJkdwMd9MHxpMA0GCSqGSIb3DQEBCwUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBiTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xKTAnBgNVBAMMIEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +vfuzumqT8vHWNOwLPnXLJitIJ7vH9leTcWq05/PfeUUyaPDbt1u9ZRFyexew+q3A +DUNKKOrbo53B+aVipxmqmfo8w6jWzv90MpnCOTEQOHeJyiBTUG99yafy9PCE2KDg +T0dXayOeKPjAoTrHDYkZmd2lA7+V+upz7R7bNDjotLW/BctVllc5fw+ky1hWAKqj +Buda0ZNElCI78U9f2aMK41/J5vLaQzQvZ68m5cgKUQ6TwWZ0ZX4p9w5aqiLRlvK+ +Wo+lYe0IFXShX73QIAlcFo+apOZG652VmMXk7vzo8llbxC6s93bwm/WxZ7K3St3O +ttcIiE6mIoG94V0Xzby/fQIDAQABo2MwYTAdBgNVHQ4EFgQUCFkAB0WIBjGP84hL +whmqKiBbyXgwHwYDVR0jBBgwFoAUCFkAB0WIBjGP84hLwhmqKiBbyXgwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABLO +dQJuW85pcOhvMf1eaMfNGaKraX/r/+a0TsbbbSDqOKhytqk7JQkGEEzyBuJhfRmG +/jXkikWAId4SBBR67FVcwyqnegzY4ObFeH22hAjfGqL1HDDu+bcpPIJzGN1CqOXB +DVjAo1tKILP2fAVHyFNGKCk60zECtXv7QfkqjMu29kMgkK0dofGIAQeYpJP1WBtH +DUTvzdjv6nBBYMLtJV1TexcSMPn4fAKDfD6uWYSeGhP6J0mfYNHOE2Sn0MuM3+yE +vrPLiCmNctnnWfcIWiUOXA9BF84MWwglK0UETR1CEONvvdpqlmvGf3G8S1r8P0MF +B8mQBTsexWGMb2NTeaY= +-----END CERTIFICATE----- +subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +issuer= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgIJAOikTZQKEq0VMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYD +VQQGEwJBVTEYMBYGA1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRu +ZXkxGjAYBgNVBAoMEUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVh +bTEpMCcGA1UEAwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDIwHhcN +MTgwNDI2MTYzNTU0WhcNMjgwNDIzMTYzNTU0WjCBkjELMAkGA1UEBhMCQVUxGDAW +BgNVBAgMD05ldyBTb3V0aCBXYWxlczEPMA0GA1UEBwwGU3lkbmV5MRowGAYDVQQK +DBFBcHBsZSBQdHkgTGltaXRlZDERMA8GA1UECwwIRkRCIFRlYW0xKTAnBgNVBAMM +IEZEQiBMaWJUTFMgUGx1Z2luIFRlc3QgUm9vdCBDQSAyMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAlZBDX6/eZDsu8JrEE3rgBcq8qRSAAhcI3B4PRz8K +NgwRGGq+8BrjsTRMqFdl8GpUDbzFKzpZ3I849GwkK+S0YU86dAQa0aORL5oEBisM +wjxDSRe3qyFZBnyJJkVo9BqF3wJhGCnWYNtFQ5OWnBKS3ZPf9jUGGL1q8ZFMoK/T +z5o0KmjH2VIAIW/Jz8Ouf3XQUHG3qC1C6l//BPqKWhxE0dYcDKBTSNMeI/NOsJ2X +PJn4g4NG+SYurQwTFYuFUTIig4p7Zu68VvgyNK/sgWoiR47AyyC9Lc5/Ojr0GMUp +zOhPEUba7ZnSgSlQLFdOyuKPO9LnaRLZ2C0uaW7ZHcXVMwIDAQABo2MwYTAdBgNV +HQ4EFgQU/DxpqnpxElyiZZHTJBmO7bTajGcwHwYDVR0jBBgwFoAU/DxpqnpxElyi +ZZHTJBmO7bTajGcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAE7VijzPBR7ouPkAQI6OEe8iWiaNE2OziZhOofai84ky +ExhGrd5NAC7eSLiIe/iXL1T0XobMwxh+hglz9zqCQ8xOLtH2z3xH97KSehnb/psM +Zz/7fOyHlj3MorRl9xpi7ncz9IY60hKAeYQ8JxSz5Wp/9KyXP+JPGCcnDal7AphW +adesPBGh6eGeAY0EDstETBsm1uMg99ABWeJk/moOlK/8F05lHUa15Pkyxx//S/cf +tKJUUcj8COm8vXvEFIBPi2FH8DKZMLFG8Ld/9N+lfDgp0r1yERxdWK4aB22iRoTL +LX97Vky4ozfdldIFBi2tzYggraPlgVn+Rxza6XXc2xA= +-----END CERTIFICATE----- diff --git a/FDBLibTLS/testdata/test-client-1.pem b/FDBLibTLS/testdata/test-client-1.pem new file mode 100644 index 00000000000..5732d03cf63 --- /dev/null +++ b/FDBLibTLS/testdata/test-client-1.pem @@ -0,0 +1,80 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Client 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIJALOPTrQGpet4MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x +ODA0MjYxNjM1NTVaFw0yODA0MjMxNjM1NTVaMIGIMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB +cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM +UyBQbHVnaW4gVGVzdCBDbGllbnQgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANj3q3KYExHaOLkrzc+rAHl90pc8/msBoc5CZ+nVHBfyCNkOllNtUh8+ +sSzpkfWJB+XqQhFCj5T9nudwnaTYHASfd9QJ/sf5ImlzP1WiKJFMbxyDMtaRkHkS +KvnMhSquZHdy3UBMKTzUBbSAMHa1YB69BmWYnaUMo6tMyURWMVYIiIR6hP/JOQEi +69iK4IHyCkZBIBim0YFaTm//DD9wMpPQ9WKQCLVvOWXkKzC46onT1B0hQqURab4Y +Tx4lbL+Ux9SfPMchw+q61H6NKDyjy+65YASXW+70WVyuAstISrrjwDVjCZ6wTmMj +KdkPrP6n40NA4s80oT8pTHGj5Qyk9nECAwEAAaNgMF4wHQYDVR0OBBYEFPN0PhhZ +LtP5w/UG720qW63BdUUxMB8GA1UdIwQYMBaAFE5kWrQCBzHu7qG5gMdamzZ0lHpS +MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB +AQB2+Wcc48txPQygh//2nxzxTCUiDhKnxXaOqJawJ1sKwLVhGYvlJQMVBWUUKr46 +xsiYl9AnQu0J0ARHDZKwYvUieaG7wVd4lnsDCifRwyUoML4lETVBucK/oTVt5Kde +hEd6VR8fnS5P5hjnfCZ9XB5EwqRk+nyQO3XFBQLjYBeCqhA9vRnIuE6xYTC20Dss +/zl4SJQRC1WQ0C1+O0719yHgujjsai9jlxLppnxZz12ZfQy7XmXJFCpkMsmI95+e +M4O/7p7RObtlVJCEhleJbc8N8aZ2JYuRENwyuvaG5z2fpK9chrP7YkwE2PMTOGsL +DS21pfxrLCsPWGFe9tLAPauc +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetyMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCqE74CmPzqGMCmWWTrCDBOShmiOcu266V0+upPBn3aEo3ZUevdRDIZ +R5waXh8sVmxoUfQdwxOBZPZFSLULPU9vQDny2DsbYfJ1MLNzEH9W5gHc5YzpvtuE +6cZvaCb8PLGcGOesACwJ2tpc+lI9ZxfB/vIO/ZUcg0mHfhcaE1bHRVrj0yZTkPe/ +yhQWWQshS6locyn+LbvayB72uaC4OeCbucGOH6B4SU1pBpG8OPMD3ktES8nIjDdj +RnuiPestjt7VIB6u37CsTSupK9Gxgyi7U9JI7kwCnRinZmHygDzLzlQ8Awksg/Qb +NuSfV0OcNG3O/Xal4OWhS1AMpayPMxXFAgMBAAGjYzBhMB0GA1UdDgQWBBROZFq0 +Agcx7u6huYDHWps2dJR6UjAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEApoV9izIHMB4418NP8FTdsToMwemxIs/ASoURGPnXnDG3EG9R8E8h64bg +38fddyaOR4AkWyn1DELzlp2vdlA4v8xY5Fu4au5MSvpxzf8H2ia3QUP/xEGj70Vg +6BJhRWQ/iOALVLVsTvJmhXQBlhIsFZMi0eIcr8CqpZ6x80SuLgl67omBvqpoVNuA +Y0FKgva+bNcEDhPur7LYAiTT+0ykgKFMR2/KnPDJOh12lYMw2ILQIQXuYCJJOXNy +nXDXa7l8vxywxMtdk5jqAB8Jct+9gV69F5uW+VuP9CEfHXpOZmDsxbFzsQbU+5mD +xj4rdSXEuk8h9bOBuqUBR9opl7jw9Q== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDY96tymBMR2ji5 +K83PqwB5fdKXPP5rAaHOQmfp1RwX8gjZDpZTbVIfPrEs6ZH1iQfl6kIRQo+U/Z7n +cJ2k2BwEn3fUCf7H+SJpcz9VoiiRTG8cgzLWkZB5Eir5zIUqrmR3ct1ATCk81AW0 +gDB2tWAevQZlmJ2lDKOrTMlEVjFWCIiEeoT/yTkBIuvYiuCB8gpGQSAYptGBWk5v +/ww/cDKT0PVikAi1bzll5CswuOqJ09QdIUKlEWm+GE8eJWy/lMfUnzzHIcPqutR+ +jSg8o8vuuWAEl1vu9FlcrgLLSEq648A1YwmesE5jIynZD6z+p+NDQOLPNKE/KUxx +o+UMpPZxAgMBAAECggEAYhz9hsCGPxhzdij81N/7QvVG+eU3F6SRSvh0jSU1ow5s +OduC1w8kh8SN0kV9H49r5uhcH/n9e96EsRwX7adcZCrIwTYT5NIgYZcgGNVPiwUJ +addacfU5Y8Z3cKavwXFDNVDYBG9sZnlqawlc4RF7+ep4rhCpmS1yBdCfYyum+8Pi +1dQsE3whxDYf+P/f/9Cjlm3UIo8hrYTOr/zq8EushX6cf67q9CxAMCHGxHDONMOX +hAv35vhOHm5WYGuuD9IitEyPFIL9F06/vU+k4uVA8Oo0/8nhl6YTHgLYZoM0dqFm +/ofz4UhDAyyMWE9nHYpXoiSIfpyC3K91ahWdpxXpAQKBgQDzTzxoLQlXyBXDouS7 +zMw/nkqDZ1oy9iOghYSrhiAmyRTFuUzGpjAVdlMaHr0QdrOVS3kEJneDciPe1Uil +Uo6LyN7Y+ePid744gQBj26vznQKe1Q1YXQ3kRVIvV6kHAgFhVMv/x5YV5JsSDtya +FRHavzDlQRjMRKxB1lnjgO51eQKBgQDkSLRM7GK/8h2r7njWGmprDqQums/ZEftQ +9m/HiraXThyJPHYzR2bE4N1nHx4SEiSYulazU3F2s+tp1YIXdDWnRquGhI9OpmcB +Q3R+Af/LIsyBl3ugzjKErHh72WIC/UhAimzLW8h3fQy1uOQOnqnbNpga1BqBl8Bv +c8TyRHgiuQKBgHXqE5tlIB8Dn9CVFAcTpHS8cN4n62XBNdjofDhoIxLI5qFAQT4Q +ckMkefexp+zfqf6Teg2R2t/AJDQs5yz/yodiDOXYjSFG6NAXbKn4XugMUqXJGNOX +HVFgawj4kbkMtsCFyOunvUvU43Yn769YwLNjbv3bw3nTWiv6Bswjwuz5AoGAZfPG +elhUHe9uqvudAdnYTynBz4KacxKhz93PvClKNLbQ7cWP8ITh8+mNV+HA10c9Cuyl +jf8lPGNMLv8I05q135gjQLdxHSoZ9KmAnEoeSEWpWfMiZoXQcApq4L0MFdOycgig +YA4EsMCrbXk+eP0JVPrD4a96iwgQDHaCrcRAdxkCgYEApyJ789WE2tQylL/1vhE6 +cu4iXMYbYTp9w3FaNZF4/yh3AT6VGyU2x28Z3PuZcWFFeZSRYuRhMNglAgSwJgXV +qJdtdKz0gscvDQxT9luqMBNKdh5u8UvwwIxoozbDH6MJ5gchk7h7mB/O/wtaE/7K +EYPR6QVzl4Mv0M2WGZ7kzlg= +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-client-2.pem b/FDBLibTLS/testdata/test-client-2.pem new file mode 100644 index 00000000000..1a99a46e7d8 --- /dev/null +++ b/FDBLibTLS/testdata/test-client-2.pem @@ -0,0 +1,80 @@ +subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Client 2, \C2\80 <\01+\02=\03>" +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIJALOPTrQGpet5MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x +ODA0MjYxNjM1NTVaFw0yODA0MjMxNjM1NTVaMIGdMQswCQYDVQQGEwJBVTEYMBYG +A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM +EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr +RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBDbGllbnQgMiwgwoAgPAErAj0DPjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMECQvC15g9BhmNov+0Wr/lRCPR9 +shOuuxR2Q5DnFHbef3ZD3wD8B6eZHJMYFXXPFI5+I7Pa37ej1iPtJZLbVM0SzrPw +B5/maTbOTIe5nkmBYe81YnE7dcDCncMlHnUqXZP7ZY14PwxA0c60dky6UYeFkQqJ +H2uzh9siX7qJKdGm5/MlK+wLEsHqg6rHYPEe0o1NHmHvwVVZTGw3DAq5z0fmbWQD +PrTUOzn/4b0bV7zZjy7tBAp65b6bOGfOccF+lvgLEhooMYaoQxYMzOIx0091cClV +FnDTdG9ThANQRp6CshOHDO1kemsdHZkFQbwn5dCWe1I0wwb7RiyNKSKyPhUCAwEA +AaNgMF4wHQYDVR0OBBYEFOQwxEN04ikMQ9IO2+SkTYwy8xMyMB8GA1UdIwQYMBaA +FI3qUMbfoAWLeyNOiULqI+o1FAmMMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD +AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQAzj35V+V7j8ScwMgPt1f5O1dkw3bVKDtPJ +10b52pXBCelNi/WNY0PTp5vOOQo5n3Cmnte6O5BBZhvRqAhoztMEhe8oxExr6dcj +dGkgXbLDhMeHJNb+qh5wezaiFH7C0cNgLEHHv6fvGIrCKJ4lqdQs+4rmDRynXBSx +Zjt18ciFDgZeCGlU869Le3C3URERGGl/whEHPyknaaJVF0mhqhYXioP83ilh/6Av +2ct9zscavCfmDGa91lDsybJqyndXmtsvFVQy+EVg3vzamx1wMNCCBCXhWv9hGNex +WJplxBSZTNbzH7g06lTcd2m98KFEu25bpyUGeaAIzOT6PtcDeef+ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetzMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCrmd2lnZGJt04Zn7ToQBf4zaBNe87YlMGc7gkA9P1fT3QBuT9K5x21 +P3SZQJj9KQ8LQwLYTUTPaJDPQ+rgZrFU9e8lTrKtjD4uPafzSDXLBDz4sDrj26hd +jh7ldMh0qMb+pvzMI+/R1oULQH0+45oweFeEfdrXeMOgITJXtNd4lPB6ezkkIE++ +xbeao2bnTfFVwCsSDuk67tEPce2CKt0jI1/8nLcs4qd3uM0bRWyPd+i6rDQckW/3 +rapPqCiGZoYLwMMVKfZMtLRftx8JQsvK+7vwU+SgOSI9Of8KYkz1nykKEUA0U2Pk +8MxOQ2SFC23ChcZMNaTWxfUQT0Enab4xAgMBAAGjYzBhMB0GA1UdDgQWBBSN6lDG +36AFi3sjTolC6iPqNRQJjDAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEAMHVjMu0aJnfmj44CLsdBVzJSFSg2ibvyRLbIEL22i5HnfAnl4uCW5ms9 +/pYIaOqw0r5av2WfeKBzarGfBC0vnMGUog7rjv6CFDT52o/w5w9ScNAE2m85Ibt4 +q9rlFSh2zOYUCfG5F+7cGHHYrWNvQ4X8p2qziUHZEl3i9hCr9edt+XnVj6Oelb9q +3zDdqOwSklJ4DzcauZORv/FwRggqZlp6upuIdVC5lIygd92k6RugQOwrWhLlaAi+ +OC6Sw45W0RG0K4WqLBrErmqH3OmV8O1xNIAFjADR70AgKYAO9mOobTRx+iD/me5R +0t0M0NLKAVEGiOTvznu/yxnNkDTpsQ== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDBAkLwteYPQYZj +aL/tFq/5UQj0fbITrrsUdkOQ5xR23n92Q98A/AenmRyTGBV1zxSOfiOz2t+3o9Yj +7SWS21TNEs6z8Aef5mk2zkyHuZ5JgWHvNWJxO3XAwp3DJR51Kl2T+2WNeD8MQNHO +tHZMulGHhZEKiR9rs4fbIl+6iSnRpufzJSvsCxLB6oOqx2DxHtKNTR5h78FVWUxs +NwwKuc9H5m1kAz601Ds5/+G9G1e82Y8u7QQKeuW+mzhnznHBfpb4CxIaKDGGqEMW +DMziMdNPdXApVRZw03RvU4QDUEaegrIThwztZHprHR2ZBUG8J+XQlntSNMMG+0Ys +jSkisj4VAgMBAAECggEASkMUfol+e2A61QT408Bm9Qa461lxqyEP2k+mr5o5xYLW +iCqVI0vVpibDV9VsbGfBARKHqiwM6BuhyTD1xAugFUenQJSaWmvND8lQqOhCfwXJ +KMDhn6/BHunCydL+ZRZn6whPip85ov1NvLqyydLmi75YkDUyYb4yB56yNpOQA5oJ +lsJVKDNwSZHk2wcrZZOf9xgBWtBvdwMy8/dpIrGi/zIVLVk8F/L/OhcKULz3xQu9 +j81AU6SiT6tYGuDaM+plUtnUvaBm8BTgk5F6ghOGnN6AmS6FNtM1v4Z3MaP8yYGu +BaUxC15RbuZZkT0d+FNqFpk5/HKKXZV4jOB/bn6eAQKBgQDfEZWGhub7NrptMhr2 +3Wq16JeO9WyWlEHhCYgRaP3LjY4JCCQKbhWkBiukxtOqOqYQ2YlKoPr6EcQHKz3C +CtMMtmBs9sSgtUeIqch7VfmgJMLBi2ONTdCDmOhw3R9oL00L3crWorHme2ZabhCU +RYUKhKQ91V/FaRlrteQHFA1PgwKBgQDdgKC+Tamw6gdVCmtf4GIwBucEgxKoFGT2 +hVb3xIyGexsb6v6e/DICmkNMzUWkZEb1m3PN3rp/76A1uLz6346x60JIotlywyA7 +9ieWOOjXtgRAPpopPrPedFhTjd/UOfIpYl9klJzYBonYC4mj0wxqdsu8niqwIht0 +wSCPtkxwhwKBgQDDrPgebx4VhheZDBftL6OY+PRh+lYL7PexQRw0MYZYVc8fix2a +bKDdJnCIdcVdEV91bm3pojcmwAjWRQ9CtR4Xi7AgRYOMehjLACyWTH/SBA03nF9y +p6zek3Zq8v3G9Z8/Zoq1JzipNtw9PUmb1zPyWxJ8Dz2YvS4IdyEVOHjIowKBgQC3 +pFAg1wTMP4tqryPMrpzZUq+v5vfN0HLQjEZJT3RufpxALjmCGHdh/wGzKDEIFXdb +Y9HEKlgXf9UlFQ/hMv38dp9S12lPI4WTEQdLHc22IhsuJHjtnyXDVlRwmZysOIqU +UxHhsvDCjdE1BIPOBkkx72IE9GFR/EUlLIVRgQOo+wKBgQCD5Hyn1vYxKZ0y4sBi +s2y1joxTZ8thkWs95HzHpv0v0mAzSU4aYdkGzoZI9edBUlutOPutPqyEjCTS+e4X +ruA9d/0zg0sqvzcC074+8LdTR8Vut8QA6Nqve0XpzxZxyQOqfouXRELUBS63uECY +XUDGwPu1yucaorm4BXqdn05Iqg== +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-client-3.pem b/FDBLibTLS/testdata/test-client-3.pem new file mode 100644 index 00000000000..eed3b8484cb --- /dev/null +++ b/FDBLibTLS/testdata/test-client-3.pem @@ -0,0 +1,124 @@ +subject= +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 1048578 (0x100002) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=California, L=Cupertino, O=Apple Inc., OU=FDB Team, CN=FDB LibTLS Plugin Test Intermediate CA 1 + Validity + Not Before: Jan 1 00:00:00 2017 GMT + Not After : Dec 31 00:00:00 2017 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:eb:56:4c:da:09:7f:4c:b7:7e:83:a4:25:12:d3: + a2:ba:73:38:51:6f:e0:7c:7c:87:e6:94:c9:3e:f0: + b2:31:09:90:fc:c7:b4:64:c9:a9:28:89:6e:6a:a1: + 23:41:70:36:6c:13:01:06:40:14:f6:c2:f6:3c:d6: + 8d:9b:42:0f:83:b7:75:9b:05:27:86:55:a0:8b:11: + 7e:f2:43:f2:d8:ae:27:e2:a9:ec:80:55:39:4a:24: + b0:9d:f8:33:ed:d4:ca:41:e9:e6:00:81:d5:ed:47: + 7c:da:33:2b:5a:a7:e0:4c:77:b3:a3:b0:dc:77:3e: + 44:71:57:ba:0e:9f:73:d2:c5:cd:a3:78:37:00:cc: + 55:1a:1c:50:45:37:f5:15:cb:b6:42:44:99:fa:13: + bf:d8:56:c2:39:7d:94:cb:1b:d7:04:98:13:8c:e5: + 52:95:a7:40:2a:da:2b:36:c1:a9:01:7f:08:5c:86: + e6:a6:86:ea:a4:29:ee:9d:e4:4b:86:d0:4d:b8:e1: + 5d:37:df:42:87:66:d2:31:d4:25:0a:5d:6f:60:bd: + 53:2c:95:ea:db:f9:d0:b6:a7:4f:9e:80:0f:54:41: + 93:a6:6c:9c:e9:bb:36:c6:cd:17:b7:20:ba:56:99: + 87:b2:86:47:f3:94:8a:34:66:dd:f4:cc:a7:c4:d1: + c9:71 + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 99:42:4d:71:3b:5c:70:1f:1a:c5:3b:6a:6a:4a:fe:10:93:6d: + ce:dd:d6:2f:9e:75:0a:27:44:a3:06:96:d9:bd:e3:33:2a:3d: + 14:80:e1:d3:f0:4a:24:f4:30:08:74:8d:b3:1d:78:d8:be:b9: + f2:fa:de:1b:a0:fa:51:d2:c3:ea:47:4a:21:16:21:b9:97:bd: + e7:72:26:ae:42:20:ea:09:2f:34:0b:3e:1f:77:ec:63:a4:10: + 34:b9:3e:9e:22:06:b3:28:c6:e3:d0:ee:7c:64:1c:3c:0f:6c: + c1:9c:b9:84:62:fb:04:8f:2c:49:a6:e8:bb:11:46:30:06:08: + 17:e7:4d:8b:c9:1a:12:74:49:c0:9d:e6:fc:89:c8:b6:81:19: + dc:1d:59:10:5f:2c:d2:c6:e4:73:29:03:8c:7a:c3:41:7c:28: + 88:97:c7:31:6c:78:18:b1:8d:cf:5a:35:de:9b:89:a3:48:9f: + eb:0d:57:8a:d1:be:a0:d6:42:71:8f:7f:1c:f5:ef:d4:00:7d: + a8:7d:e7:03:2b:3f:bf:50:63:e5:72:57:38:ae:50:5f:94:42: + ea:4c:f8:02:b4:33:67:e8:09:bf:9e:21:01:48:63:d4:cf:29: + da:c7:8e:21:a6:ae:1f:e4:56:03:74:c8:2e:3e:77:9d:60:e5: + 53:4c:14:87 +-----BEGIN CERTIFICATE----- +MIIDCDCCAfACAxAAAjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK +QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU +TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwHhcNMTcwMTAxMDAwMDAw +WhcNMTcxMjMxMDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA61ZM2gl/TLd+g6QlEtOiunM4UW/gfHyH5pTJPvCyMQmQ/Me0ZMmpKIluaqEj +QXA2bBMBBkAU9sL2PNaNm0IPg7d1mwUnhlWgixF+8kPy2K4n4qnsgFU5SiSwnfgz +7dTKQenmAIHV7Ud82jMrWqfgTHezo7Dcdz5EcVe6Dp9z0sXNo3g3AMxVGhxQRTf1 +Fcu2QkSZ+hO/2FbCOX2UyxvXBJgTjOVSladAKtorNsGpAX8IXIbmpobqpCnuneRL +htBNuOFdN99Ch2bSMdQlCl1vYL1TLJXq2/nQtqdPnoAPVEGTpmyc6bs2xs0XtyC6 +VpmHsoZH85SKNGbd9MynxNHJcQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCZQk1x +O1xwHxrFO2pqSv4Qk23O3dYvnnUKJ0SjBpbZveMzKj0UgOHT8Eok9DAIdI2zHXjY +vrny+t4boPpR0sPqR0ohFiG5l73nciauQiDqCS80Cz4fd+xjpBA0uT6eIgazKMbj +0O58ZBw8D2zBnLmEYvsEjyxJpui7EUYwBggX502LyRoSdEnAneb8ici2gRncHVkQ +XyzSxuRzKQOMesNBfCiIl8cxbHgYsY3PWjXem4mjSJ/rDVeK0b6g1kJxj38c9e/U +AH2ofecDKz+/UGPlclc4rlBflELqTPgCtDNn6Am/niEBSGPUzynax44hpq4f5FYD +dMguPnedYOVTTBSH +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetyMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCqE74CmPzqGMCmWWTrCDBOShmiOcu266V0+upPBn3aEo3ZUevdRDIZ +R5waXh8sVmxoUfQdwxOBZPZFSLULPU9vQDny2DsbYfJ1MLNzEH9W5gHc5YzpvtuE +6cZvaCb8PLGcGOesACwJ2tpc+lI9ZxfB/vIO/ZUcg0mHfhcaE1bHRVrj0yZTkPe/ +yhQWWQshS6locyn+LbvayB72uaC4OeCbucGOH6B4SU1pBpG8OPMD3ktES8nIjDdj +RnuiPestjt7VIB6u37CsTSupK9Gxgyi7U9JI7kwCnRinZmHygDzLzlQ8Awksg/Qb +NuSfV0OcNG3O/Xal4OWhS1AMpayPMxXFAgMBAAGjYzBhMB0GA1UdDgQWBBROZFq0 +Agcx7u6huYDHWps2dJR6UjAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEApoV9izIHMB4418NP8FTdsToMwemxIs/ASoURGPnXnDG3EG9R8E8h64bg +38fddyaOR4AkWyn1DELzlp2vdlA4v8xY5Fu4au5MSvpxzf8H2ia3QUP/xEGj70Vg +6BJhRWQ/iOALVLVsTvJmhXQBlhIsFZMi0eIcr8CqpZ6x80SuLgl67omBvqpoVNuA +Y0FKgva+bNcEDhPur7LYAiTT+0ykgKFMR2/KnPDJOh12lYMw2ILQIQXuYCJJOXNy +nXDXa7l8vxywxMtdk5jqAB8Jct+9gV69F5uW+VuP9CEfHXpOZmDsxbFzsQbU+5mD +xj4rdSXEuk8h9bOBuqUBR9opl7jw9Q== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDrVkzaCX9Mt36D +pCUS06K6czhRb+B8fIfmlMk+8LIxCZD8x7RkyakoiW5qoSNBcDZsEwEGQBT2wvY8 +1o2bQg+Dt3WbBSeGVaCLEX7yQ/LYrifiqeyAVTlKJLCd+DPt1MpB6eYAgdXtR3za +Mytap+BMd7OjsNx3PkRxV7oOn3PSxc2jeDcAzFUaHFBFN/UVy7ZCRJn6E7/YVsI5 +fZTLG9cEmBOM5VKVp0Aq2is2wakBfwhchuamhuqkKe6d5EuG0E244V0330KHZtIx +1CUKXW9gvVMslerb+dC2p0+egA9UQZOmbJzpuzbGzRe3ILpWmYeyhkfzlIo0Zt30 +zKfE0clxAgMBAAECggEAKZNTfGmVHb6FEUicbBkLdJkKaAi1xghSEgBTFVV8hYFF +JgM58VxXEBxHb3sMOXFTjABHfA0yJu4GhnT5C2a+SuzMUBi3BNp3xFnQ7YfnjTQn +Q7n1WC62Mk0NDrtcoTWojcB+q7REUOkCO6IylI935sp26AJE0KQlf0iaTB8LXT7R +/IeZgUs0qoY+LVa+bsCgXvvFqtf1U3GHgW6R6vNksWAL/hDewJPAo4pzoZYRc1No +TJ8lP2fBNTzn+8zkaoHJ6awT9T0TMlwq0uruDr0wGeSTYpuwP9IuwDdByiJO6EQs +7XNTUlEr5yjfIpR5pnKRxFwlrvPl8xpW32gQgZYoYQKBgQD39P5NEquNM+CNM9EB +381du6bk3kFB2bn49U9jGbU1kcMJekJ6cOsGQgZvtmh7GWkGdey8icjOCxx/rmQ8 +Yk05XD1/qPk1H1EQ0xjJ0NheHSq1eT3L4ZAR9hvCZa7Kn882VgKTAHAnp0XxoIse +mF5TVCPTxHzl1hd9V5ocRinXBQKBgQDy+IM+axVIuTIP2KUs19JooZaSx2k9k575 +7utNCFsvYjA9Gi3QufkBGZ+zoQjwJGKyQbYdldgObMivhylhL/YAsfG0OdZYEMLQ +NF9xHy4nLI/6ikhb7Tm1Ja4iCrEj7H78TLz7U3V2gaOTrpVWg/x+SXg1wz6gMH1p +r2Vm4wZcfQKBgAMD8rfDO1zbPabZ83e/XgL+zjfIUCjZ2f4iFZYR8Pc2z5Vk7DwK +9YC8+tEO+dRNac6PTpK4Zj3DOtR+rW9ijqfc7E6KIoW33MeEma+Sn579YdCVKeA5 +3QOq54mErQx/xwhVIXDiN3rz+NDIcAiuN69xvYHMyOwOn3iBwTkIzWd1AoGAaDUF +tSOgQUyMmBdG4HDAIXgMaG92R56ktHUwHh3TXgMyxUIRHFBm533h7NzIRJaWbfWY +8h9uRuGJQR8rNC+pxqqLC80yjRrDMyEDPjUtSkfmiIH+fs7DzVmX0ancR5gK7pLL +Cs/EtUrsqU5bZnu9Mzy3ffd7SKEDaOx9BR+hftECgYEAs0wGUVW5oZoaHzHorHfG +W4dOwvmcT5Y2uHP8JBiOr3l6NHzdbOhN9DmEo62sf4rIrz+h1TIkLCS36Agm28JL +ptTfVAaB6ufGwBh8uMtzL5cd6gZgG2g9SKEdHCIQ9eQajuepWEgtMJda5e1+zLfB +Na5+X0YtsCTXKtqSMSO0Zy4= +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-client-4.pem b/FDBLibTLS/testdata/test-client-4.pem new file mode 100644 index 00000000000..8abb2e10f02 --- /dev/null +++ b/FDBLibTLS/testdata/test-client-4.pem @@ -0,0 +1,82 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Client 4 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 3 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 3 +issuer= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIJALOPTrQGpet6MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMzAeFw0x +ODA0MjYxNjM1NTZaFw0yODA0MjMxNjM1NTZaMIGIMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB +cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM +UyBQbHVnaW4gVGVzdCBDbGllbnQgNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALhJzjx42rEMT6JQC2Dq/A2/iHWyNCW6h6PqLE6JsKsHiUIHLQWbE766 +Pskwg1RpXkfMONyOYVpGSos8jDdxNGbpyCsRFsMcmG/oiGKUY+OCW7S0l5lvSQqX +j18LOTAThSXWZoMvimMMNFivH356GIxR0mTFdv308JR3GgI1dZvvIKvNQUjs/bvG +UUw8Gq9PxbHGLjQFLFYaCPUs8OaO2nTEurM5JpDlEKaqL2usOJgjhvvtlJ4oo+10 +aYNUlIotYxbJsxnAIciR6FjNfUz9z4mrYG62iR6OD4bI3OP74nOv6poim1tze7DP +93E/GEHso+Pfj37m53r50TD+GUHwRJkCAwEAAaNgMF4wHQYDVR0OBBYEFFRemg/3 +R1b0u9qfeB6LQFAc23awMB8GA1UdIwQYMBaAFNJkFNeVtJApFs//7Jvd5R/+BJ0Z +MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB +AQBHrHzTOjZTjWnmTCmUwtDfSXEVQia9DtoaI1dVUDNbANFvMQuAe+ReuEicWmxQ +mhCXkFBgQr56g5QOY1e5ncBpjSqs9GYfheW0L2dTYFvVp+yXAaAVW0sY56dFnrKx +rYiPYO6ssMElGQPUQs8q/Mup/JMvkv3B4fNi+COPy+KNV6t7pzIEydg5uurOkK8J +kwIvyFlit1W+KXIzZlKHCzv4jF0Q3lDXQXjNoLORCAxSgyJVVcxvLzPTtCtKM0XC +0uMc29N61aTmzTonxdoAlxhRhohCfYohTvUg/2JjbUb1U/+VRRkTDl/4H4DegfBm +igvwRnL+koW3Bp8ZdmRfTO88 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIJALOPTrQGpet0MA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD +VQQGEwJBVTEYMBYGA1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRu +ZXkxGjAYBgNVBAoMEUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVh +bTEpMCcGA1UEAwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDIwHhcN +MTgwNDI2MTYzNTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK +QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU +TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDMwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCZ3UPCarKiS+IqK2VvB0lVQ3L0TPuNUov54L7ELFnB +1lijZWX2wX3Vi0uAyvwmLi+PYkUkzhaNhGWbVPHEFqsOzwMbifGLfdxjetZD0kOr +ZuARithrDXVWZegIXEFDUdnpz74TKG6rCQ6o4WMIFUV/j9770iMtrMABr/BwASrE +sVqAqNe4bOqHJyxyeTxF/vCRme3PVf+u06mM9OLxVVaPRK+tiysdKprjs6oyFoxT +S/Cl+vv7Lnt5Yth0F7YJGMrIKDPuSWCqAfKWAc+b73XPQ14coIczVzQAikyRFmGa +9aaw0aWYI2gjTkuah0c5fboZAuHT0Z7nQVtYUyIYRY8BAgMBAAGjYzBhMB0GA1Ud +DgQWBBTSZBTXlbSQKRbP/+yb3eUf/gSdGTAfBgNVHSMEGDAWgBT8PGmqenESXKJl +kdMkGY7ttNqMZzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkq +hkiG9w0BAQUFAAOCAQEAdoyvb/2+r587RMar6+/OauAVXWwnNEI8hhUvxiTnDq/M +TKu+okSAHSbGvYp5yGD2IXlEZHjtKnMapsih7tvm2VboM7U5Zi3aK7hjvk6Kxs5B +eynHP9dDnUWG/qRbAF83B3mK7ymmrZvJIDwHZEtY9Gmmmb9ty9aK2ywmYtlYgIS8 +FSh/qwzvThIiMuCQma9JJOx6ZDXV2YwCxYtIRAuTvIWuOckb608fPi812k0zYILG +q4VLhZTc2AM8H/629RBRV5zC/tqySLb3VgpWQ2vZSzUwuKIdIPs4lsbNqmsdWIyR +hHnl8610t/JAUdCPHDd1bBlYNgLhMKJTM8fGu9Ux/Q== +-----END CERTIFICATE----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIin+Bh1kSGiQCAggA +MB0GCWCGSAFlAwQBAgQQecgfNmQfm8gVISC1QceptQSCBNA3fAw+SiU1l/j+Pc05 +2i1VzOkDKQGTSVtpO6tV67p7uWaUS2oCpx36eE1LfJgWOxsk+gizPsH1kFrKlqfl +6SmeVp17dogeBX/uf9bGqyqEO4GXEmxhajMFpE8mmQJ4aSLGuSN2sh1YkI/QrX/v +y6QsJi0hsd/xxjPORoZxhtgRwRaiw2hUF9Al/ipusoRQ+x4of6j6hpVLxAn6yKIk +l6fk+HDlGaEBLZjbTQd/Usx6AlQo5ydEPKdftKx6uHltkt0hrnIJC6pc1w6O3Ndh +An/lagHxNSGfAFNkEH3YCFYtRzAR19a879LKVFqaKfVgfhwv8e0x06GaIfvyaGiY +KbzRkjIUp+Z/0SB8hZVzOTNYcW/0cXRNH498Vv5L4j0irqTyf7yxwiPuWZieBGpa +sLo9gE3Ap0XahS6//G2ZIXAwXN9nVPN4QjyKh4iXmYO0arNiIYZwtHJ77E/O62x7 +cKQA5y4Fsgft0wWUfAi5nZ16qwleXpSMBfnvHMz5RJWIGjpiQ67Mf9u9svudXR0h +5TQaViQLDUVs6kAhulTQvdifPCsIbpLlJ2YxH2L9BsTPnU5oHBU+veFA6zagjh0G +UtyaQWKe8iC7cjeEXRL5s32bfaG4x5FMnNJbU6Sd5qGbFHPJyGorKOCFh8RbTlZV +pXbnYFm8KX1hRu3BbCKxHUVypqyYj7SIOe5ihWkudDq1qDS+ugzMhAdZK+WF+euv +Hzf/HYI3zLKeiZmhmG1ysfs4SRd2K++dYIbmWcFDcNLogsYt4BidH/8VZdxsmd0C +vjt1mY3W3QvV7gWr2ZeMWalmrS+OuTR2jcJPxrqqm8Iz4rGOi/yRrp4i9F3lNuGB +TMo1RP8SFFh9wXvwxRqh8/uWKdeytgqgNbXAA9Lu93IDniGH3ELM655mxbEHFR5W +omCVKu7LTNCgCx1gJDd4zb9cNz0Fmn8dFbdHcmMecil01sDITq6pJRp/niB8MFx9 +Fp8t1wf9FrPVbZ5+yKTVfAms3xgj9NmEs5pBZNbz/z4PKW5WY/fObSAiIwNp+17S +mkFE65YPa93DXjs4u62rP21i5L6e5u7rE/hpw0DbWLpkcYrVUrzoEfWXLnBpcCam +OexWbAZMmp2MWF9YOVsGmAfgmPpE57SPvmuh48vFZfnx58hz/Ejg0MUrtvgRzy0X +RcaSX1AJUJ/+M/qsMNR6AX5z2Ofal1nZj5/lfq/OS2lakn2SbZPczuFb87QjXTuj +GXARzXVlnj4CFHO8mep5Z8SsLHOj2Fdljk3EWoNvm2yE+7nbKHVAeDaEqA3lf1Qw +jZsUX3Smc7jc0hRccvwsaNLDST4H1uorCIqGgtqRhH/FsH7jlmqLi2+CaLLd/KwA +VOJoSLaZP3pWJcF2r+sNfrgsFkK3Pog9BRVt45QCarnnmFKu5PmtGnXDw4kc7eVG +z7on+oRURza5mo0HNZTe+JaJO3FdO5fULxn8kk2JpEGqqSbpT1PUCjAc9oShvV7b +qtYMyFLHn5KBJ64HUdQkeDq7o1s2rmQwbIt48xGRYbUuINFBC2KS4oTPlI8jPuzp +KKWC5TGywFtdiIJTWKyPFiHsOM9nAFv+t/rFVkzThrTPlg0W75TQWcK588Id2yzL +bzKKwKV8N3LcIgVcr+bZHooJbA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-server-1.pem b/FDBLibTLS/testdata/test-server-1.pem new file mode 100644 index 00000000000..3eff22ab778 --- /dev/null +++ b/FDBLibTLS/testdata/test-server-1.pem @@ -0,0 +1,81 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Server 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIIEIjCCAwqgAwIBAgIJALOPTrQGpet1MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMTAeFw0x +ODA0MjYxNjM1NTRaFw0yODA0MjMxNjM1NTRaMIGIMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB +cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM +UyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANlALdHnBWuhYm5bsginazmSRHO0l0lpyjbG6EdlPFLg/Xb7Q3tILGRd +J585iqhCLRRx/HZ0j+yv1WZ3ZFcOEZMessw32kt0LN1fbYPrKkHVkQtZjorxnHWa +IW/JMBvMCZHag9vLI87TRtjMicB9ZxIl4Sppal7FZ0ggYBlOZ+T5G3j08y4tszrr +3Xg4BOuKziSmgQrTBObOG6IvsxZZv/MtwnvPeIowBUOGaux5CIjyotmaUxxaRlH9 +jbVpojtYCDskwSOq5faqPSpaoOKyXLQ87ayAL6nAP2dXvcv44kaaZbZ5lSyrAW3P +hMsGEhhhkdHfrG6ACwHV45k/m2r4kUkCAwEAAaOBgzCBgDAdBgNVHQ4EFgQUw4g/ +xtD1EMkhSpS8jqKa/15emtcwHwYDVR0jBBgwFoAUTmRatAIHMe7uobmAx1qbNnSU +elIwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwIAYDVR0RBBkwF4IVdGVz +dC5mb3VuZGF0aW9uZGIub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQCbNF+pt4RdLkyd +fC0mNxctQeCTIWfoazpZxNrku0gmQmlWnsY8J9TMhF0Mfd3tePXGTsPRyybWObIw +6IKvYCvK8mgz/BlB4RSWs/evVuy+z5hFeBQJJ66dPhfb4gnLw9qnmpXrgudvZkMM +F6pjsNAXxj7yXX5PvgLg+0VysElquwviuQl4AQ4Lit57U+TlLlkRFcTxbb1pZAAK +4q9SWf6rTqwBW6qr8YMbInRZ//cUfprAbDJ2rS6bP+X2S8Qftl66iNOJkhkbRosy +NVn44DGUw5UHL2T4g6FhziisHoga7rMRqMT4Kmo/xcFzRPbP7jggqV485JyLmxXw +vfypbIxc +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetyMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCqE74CmPzqGMCmWWTrCDBOShmiOcu266V0+upPBn3aEo3ZUevdRDIZ +R5waXh8sVmxoUfQdwxOBZPZFSLULPU9vQDny2DsbYfJ1MLNzEH9W5gHc5YzpvtuE +6cZvaCb8PLGcGOesACwJ2tpc+lI9ZxfB/vIO/ZUcg0mHfhcaE1bHRVrj0yZTkPe/ +yhQWWQshS6locyn+LbvayB72uaC4OeCbucGOH6B4SU1pBpG8OPMD3ktES8nIjDdj +RnuiPestjt7VIB6u37CsTSupK9Gxgyi7U9JI7kwCnRinZmHygDzLzlQ8Awksg/Qb +NuSfV0OcNG3O/Xal4OWhS1AMpayPMxXFAgMBAAGjYzBhMB0GA1UdDgQWBBROZFq0 +Agcx7u6huYDHWps2dJR6UjAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEApoV9izIHMB4418NP8FTdsToMwemxIs/ASoURGPnXnDG3EG9R8E8h64bg +38fddyaOR4AkWyn1DELzlp2vdlA4v8xY5Fu4au5MSvpxzf8H2ia3QUP/xEGj70Vg +6BJhRWQ/iOALVLVsTvJmhXQBlhIsFZMi0eIcr8CqpZ6x80SuLgl67omBvqpoVNuA +Y0FKgva+bNcEDhPur7LYAiTT+0ykgKFMR2/KnPDJOh12lYMw2ILQIQXuYCJJOXNy +nXDXa7l8vxywxMtdk5jqAB8Jct+9gV69F5uW+VuP9CEfHXpOZmDsxbFzsQbU+5mD +xj4rdSXEuk8h9bOBuqUBR9opl7jw9Q== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDZQC3R5wVroWJu +W7IIp2s5kkRztJdJaco2xuhHZTxS4P12+0N7SCxkXSefOYqoQi0Ucfx2dI/sr9Vm +d2RXDhGTHrLMN9pLdCzdX22D6ypB1ZELWY6K8Zx1miFvyTAbzAmR2oPbyyPO00bY +zInAfWcSJeEqaWpexWdIIGAZTmfk+Rt49PMuLbM66914OATris4kpoEK0wTmzhui +L7MWWb/zLcJ7z3iKMAVDhmrseQiI8qLZmlMcWkZR/Y21aaI7WAg7JMEjquX2qj0q +WqDisly0PO2sgC+pwD9nV73L+OJGmmW2eZUsqwFtz4TLBhIYYZHR36xugAsB1eOZ +P5tq+JFJAgMBAAECggEBANOvV3Y5tuxi+jgyHM2V5ELapNxhUTB1ZlXthpNar2bK +V+9BcUWE08yPgHYDaXn90VMkpiz98udbhYIsP5cb3EAQH4QhwBDZiIXD4EA0Op6f +bI80mlIEjsGAh6gWz0umioSV+PmHfMKg112UH31yM6ZZRoc+5CioDLUY5sqBvXY7 +AGnxMslXEesIP9VLzautKJJGoCXyZgoEiOpUn1N69BcupipekFNlsIANwqkktE+i +gfq6xC4pxUx8w9wkStVg0DPiasl8cOvD4Cle4Sk30itWhIzQnB0uoUcfVsjiuiuT +kGt0pboQyGrezSsbyjh4vo6kAABc1oxeeNIVh+BHeqECgYEA9illuy5qwcxaHdb5 +g06WIpFk+HsX2yN8wH3FRoDwU2YfOWSP4+bsyCD6KRzfm+Diyucb7B9aLq+k4FvZ +rRhgVvoaGLzLwqU1ue5EQHcmcNabGhJ2kzp9/MeyWa+zxe1l9ugjdgIQt8HA75xI +dMFSPCfCFmXQVP761y5lWSFTOFUCgYEA4e76jMsFY7WEoMNgmiIzN0uk8b6Z1U9W +kmW7ARrRlbjIaBHJCvblakCIir7BqO7Qyw7E/ssbGTBG7KN+ZxhB17RgAzQla5b3 +VtXQ+wTLbsaL0XSjTvGJcseKRGpLA/9JT6krcDUWgWWp9Za+XOqE6138A1S7Uzom +d5Kq4clMuSUCgYBk7e5XRB3jnh0o9PbTHsqyG7kxpI7QfAlvh2H2mXFwUIPm7VOO +FyXonJTVfJA2gouatA2fz+8TC1Nn5mEm9sKxhtRiZi6JfWG8/XOglvn89BEeLInP +rgDwPxTibwcf1UmkZeP8Q7aFEX1BAWw36vgD0C1lL5Ou9ej1h4FofSB5DQKBgQCB +d8OFuLVO7O6ifTM3QA/fSO6tRQ68KKmwXLFrsaWWSXw1/rMZIGBYocBc2XD6UFe/ +dKg4ZcUSixKpegruIv1gEVHPpCspWypDTWVi4t18jjQDA/atP0YHSwcVq19vW/Pz +vTpSI2nRHqPlQsu1TQJ8KbBHbF+oErOGY7qehg78LQKBgQDl3/srSZihKYwva8a+ +FQrZ0978WwD9JsUOFuHp8gQzdArFiK4VCCv57VTL/4cqdVOwW9eDhgeP5cKnAQ99 +5HZzLl+L0b0QfGcerPn4002Us09g7eAh5vHHY7BpdXOU9a9lgfOVWEIMWp1NSGEc +vd2TbSD8vDjbd9hZYh+pqkddCw== +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-server-2.pem b/FDBLibTLS/testdata/test-server-2.pem new file mode 100644 index 00000000000..349abb4e2ce --- /dev/null +++ b/FDBLibTLS/testdata/test-server-2.pem @@ -0,0 +1,80 @@ +subject= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = "FDB LibTLS Plugin Test Server 2, \C2\80 <\01+\02=\03>" +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 2 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIJALOPTrQGpet2MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMjAeFw0x +ODA0MjYxNjM1NTRaFw0yODA0MjMxNjM1NTRaMIGdMQswCQYDVQQGEwJBVTEYMBYG +A1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRuZXkxGjAYBgNVBAoM +EUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVhbTE0MDIGA1UEAwwr +RkRCIExpYlRMUyBQbHVnaW4gVGVzdCBTZXJ2ZXIgMiwgwoAgPAErAj0DPjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6tYgoMCdvU9QQT556tSgaAGtkf +u2IMZZxoqXqK1Qpfmgg+PkG8+5Dzf+ExdjQUUSS8fPf8AMFx/ZIAMZeNEOnHYPLH +p8iWbIDYyoqqI089WQ5yZ5lypqLvdiTt9HfgaL+rI7fIPNlcGiwdDNq4I9r7rtGv +SoY2ZajgR5hVMessAhVEgb/ahUXlnF2qpuzaSKJMpJU+eCpWO629nNnDCWbJpinB +B5xqhXLdf+clHqdwWtGQVdWTxFOLgy3KAw947FY15hwhZvPSAC1zV/bgaKdh5Tn2 +hXCG4mLQUj/vIX0j285zl0B1i+1tkw+2saUZq+gwvRkV9y2kXNFBlPEcZasCAwEA +AaNgMF4wHQYDVR0OBBYEFFItD8P8VeKOiRbgLBAIROtUHTOnMB8GA1UdIwQYMBaA +FI3qUMbfoAWLeyNOiULqI+o1FAmMMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD +AgeAMA0GCSqGSIb3DQEBBQUAA4IBAQAnVKze/bSM5Azr5Ig/QfpLTEMSiZJ/A8cM +99EnThay2ZuF+1hUA3JMldI7pgOqCtuTp7BOI1UsMs+KdtJh1eMx+HQ632gf3Hv8 +9k5Q1cONV/mxZywwfBItFf1i49Tj0NXZZKiSyoiUoDaiWMHdpXTdWSu05lpx8rDJ +tcQrrxRFSc3togqvs1+PW+F10s3x0uv5ocHLZ889vx6P8gVleKKP/O8vGOBc44AO +SS/oBHDwY8Q+1blG3tCbSgVpZ1SF6njnMff4fO+LGS8W5aCcHaqne+CeqH+b/GxZ +NUr9Xwh/OjDE8/REGkOwxywvMJEhI9aLxgW7a6yjcMG7HH94pZ4f +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetzMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCrmd2lnZGJt04Zn7ToQBf4zaBNe87YlMGc7gkA9P1fT3QBuT9K5x21 +P3SZQJj9KQ8LQwLYTUTPaJDPQ+rgZrFU9e8lTrKtjD4uPafzSDXLBDz4sDrj26hd +jh7ldMh0qMb+pvzMI+/R1oULQH0+45oweFeEfdrXeMOgITJXtNd4lPB6ezkkIE++ +xbeao2bnTfFVwCsSDuk67tEPce2CKt0jI1/8nLcs4qd3uM0bRWyPd+i6rDQckW/3 +rapPqCiGZoYLwMMVKfZMtLRftx8JQsvK+7vwU+SgOSI9Of8KYkz1nykKEUA0U2Pk +8MxOQ2SFC23ChcZMNaTWxfUQT0Enab4xAgMBAAGjYzBhMB0GA1UdDgQWBBSN6lDG +36AFi3sjTolC6iPqNRQJjDAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEAMHVjMu0aJnfmj44CLsdBVzJSFSg2ibvyRLbIEL22i5HnfAnl4uCW5ms9 +/pYIaOqw0r5av2WfeKBzarGfBC0vnMGUog7rjv6CFDT52o/w5w9ScNAE2m85Ibt4 +q9rlFSh2zOYUCfG5F+7cGHHYrWNvQ4X8p2qziUHZEl3i9hCr9edt+XnVj6Oelb9q +3zDdqOwSklJ4DzcauZORv/FwRggqZlp6upuIdVC5lIygd92k6RugQOwrWhLlaAi+ +OC6Sw45W0RG0K4WqLBrErmqH3OmV8O1xNIAFjADR70AgKYAO9mOobTRx+iD/me5R +0t0M0NLKAVEGiOTvznu/yxnNkDTpsQ== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDurWIKDAnb1PUE +E+eerUoGgBrZH7tiDGWcaKl6itUKX5oIPj5BvPuQ83/hMXY0FFEkvHz3/ADBcf2S +ADGXjRDpx2Dyx6fIlmyA2MqKqiNPPVkOcmeZcqai73Yk7fR34Gi/qyO3yDzZXBos +HQzauCPa+67Rr0qGNmWo4EeYVTHrLAIVRIG/2oVF5Zxdqqbs2kiiTKSVPngqVjut +vZzZwwlmyaYpwQecaoVy3X/nJR6ncFrRkFXVk8RTi4MtygMPeOxWNeYcIWbz0gAt +c1f24GinYeU59oVwhuJi0FI/7yF9I9vOc5dAdYvtbZMPtrGlGavoML0ZFfctpFzR +QZTxHGWrAgMBAAECggEAKgnBxdE+/0gv64rx9cHB0wlHMUzkfS1/zB+nEh1Xzp7u +C1ujLYLysCIjOO+0SRH3BMhqHmd1Xp0MM2XzViUTgImMatZ9l9fevobwjaHP3EoK +4H+H/b/y16njnjeHIoIVEQU8okcTaJlPRsvLEs7yCcTgkrAUKTV1jFwMYClJRIlB +tH3IwlTc4X4ISLs8jU5TbDwbX/vsfOL9dbhnI0pRJYbjx0A6bPeIwPcBv9MrJ0zC +J08v6CtBtfhg/rr0Il3Shhr+he74R6r9C82095XtdPgV2RdXrwD3VZfJ1N+VE2g4 +xdIQI4TybL7olsduwDz4EDcAkLLQNuMQIJrvmYtDMQKBgQD5dp7whs70bagEIhNc +sviVpOzbwzpB4jjOaJuS+SpTYy6xiZ1GSXokjAlGnx27YJx2F0XsGNbD8YEJplrW +pl6xxxJJ76ClpLMJ4dOaUZICaOUJlvFs/a/eq1DPzm/8de8VnJPxV8NHURGoItjD +Cp5J5Hfs09pAoGed41OU4H1VaQKBgQD07mjzDBOiRMgIyoQqkO7KSUy4VxPWRw/e +UURwkWlDPyb69x8VMlaaVNvtA+C7bHGGNCxQEcl9hy/eFNeLvxZZnB+3CG5BBIfT +ZFXwDLlt4Cck7lT8RmGCHmD7uEeLsKlFXLmdIsGdy4vb5UJoluU1lonYdfMmF1r5 +Wn1D5Yhb8wKBgQDxyxnvBJtfq1Go/+ZIeWgck4jI0YeMCFsLJbV1VXDC9mVxy3LX +h6yN8/Whr+Q/lDrS82fgmYrAzTpHQijV2Nf79Hozk8HgC61FrM9OyjV9hHJLQzSq +cZGff+mKIgvFZgHgrq1uxaQPYfJ9R5lWItqdwIbE0+q9rTwBey+uI5EX0QKBgCQA +WxrGfqhOVdy877pV/VNsfMNnwTmwzktORLILHZ7NzIv9AOubr5EVwhHPjRvfb7xb +NNw8ERLLaSTKiXg4CS/4u8yqh5I0+UYALIWRSTw2Q2qxQXqnWbPHhVfamWGWAPt6 +CqpnLnBkN9WFmEJJU72RuAdCJxBFRJD8VQdC2M0zAoGBAOpbXf1MPtwQIOrBsKiw +pvlRzVKuW4fo8LNqkk/m5f+hR7kptfc2uDU1HiyOjsrsh1AnYD+sg3bH1XGs5Ai7 +y5+C0hQHQln+HWpe/5Pw6IPRsgTHRRkKYtS13QyDGHybU+Pq8ZnY20POOBZyMLqo +jKg/enqxtKZ6M+U4HcjKbewH +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-server-3.pem b/FDBLibTLS/testdata/test-server-3.pem new file mode 100644 index 00000000000..9ecefed2918 --- /dev/null +++ b/FDBLibTLS/testdata/test-server-3.pem @@ -0,0 +1,75 @@ +subject= +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 1 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 1 +-----BEGIN CERTIFICATE----- +MIIDCDCCAfACAxAAATANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK +QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU +TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwHhcNMTcwMTAxMDAwMDAw +WhcNMTcxMjMxMDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAqMXM+ZMsh7LrUrGS1Kw6z4eTccaITGxJ2bdPpPrerwQG/2lj5N9kl/YUAUwY +d/iCmc2ZiZ0xf12R39yDiZrzn+QSArmgKpXRoGStleTRw3Zr5KMO9PhCrDrNTP05 +a95y6Pi+GFn/xG74P7dg9JVcmKZ5DS9LKheLzR8wbYNQ28BEq4ljVJ5FCXrSqO1v +tQZ/dK8ThZw64ODFhKNFTTspk6VAecnIG6ABU3E9EZ038423q6OZERBXlHU93Y8F +IGlzaD4zKEGhzbmAdG1+Z9xq6DcuQkrIUzUgHVljIpMGCXzNe7tkXYw29lAapiIK +bcH9GBM6hg3p18SZBsUmYkslhwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBohEcs +wqGHDfn756yZTwX8415Xm4qC9MqcA50I4JGk9KqeUxLCytultTvNIkFMm12TyqAK +lMFK/GFvwJwlz+dBrsraaosw2HVZs9e/kHJA70R/iNwhXb0RUIJrBxzE4M9gQ4I5 +I9DBo8hoaaPU2awHE2Y694qW4dEdFXz2637Afnb/euJ2zm3nhrV05pKu/Wer9cjF +W8Opx2AjgB+/ixftVQuGr3fys0tvhpZlC7MYnFvKFlh6QFvFCSa9M5ESHrnuRg2g +sqDTqdWab1drQvWZyiIBATErMlgQxsAQMT8YH4U4qbGcicYh8Zw4ZIRyHZndMHIU +wgO+WZp3DVHg6Y81 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIJALOPTrQGpetyMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEpMCcGA1UE +AwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDEwHhcNMTgwNDI2MTYz +NTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh +bGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwKQXBwbGUgSW5j +LjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJUTFMgUGx1Z2lu +IFRlc3QgSW50ZXJtZWRpYXRlIENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCqE74CmPzqGMCmWWTrCDBOShmiOcu266V0+upPBn3aEo3ZUevdRDIZ +R5waXh8sVmxoUfQdwxOBZPZFSLULPU9vQDny2DsbYfJ1MLNzEH9W5gHc5YzpvtuE +6cZvaCb8PLGcGOesACwJ2tpc+lI9ZxfB/vIO/ZUcg0mHfhcaE1bHRVrj0yZTkPe/ +yhQWWQshS6locyn+LbvayB72uaC4OeCbucGOH6B4SU1pBpG8OPMD3ktES8nIjDdj +RnuiPestjt7VIB6u37CsTSupK9Gxgyi7U9JI7kwCnRinZmHygDzLzlQ8Awksg/Qb +NuSfV0OcNG3O/Xal4OWhS1AMpayPMxXFAgMBAAGjYzBhMB0GA1UdDgQWBBROZFq0 +Agcx7u6huYDHWps2dJR6UjAfBgNVHSMEGDAWgBQIWQAHRYgGMY/ziEvCGaoqIFvJ +eDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUF +AAOCAQEApoV9izIHMB4418NP8FTdsToMwemxIs/ASoURGPnXnDG3EG9R8E8h64bg +38fddyaOR4AkWyn1DELzlp2vdlA4v8xY5Fu4au5MSvpxzf8H2ia3QUP/xEGj70Vg +6BJhRWQ/iOALVLVsTvJmhXQBlhIsFZMi0eIcr8CqpZ6x80SuLgl67omBvqpoVNuA +Y0FKgva+bNcEDhPur7LYAiTT+0ykgKFMR2/KnPDJOh12lYMw2ILQIQXuYCJJOXNy +nXDXa7l8vxywxMtdk5jqAB8Jct+9gV69F5uW+VuP9CEfHXpOZmDsxbFzsQbU+5mD +xj4rdSXEuk8h9bOBuqUBR9opl7jw9Q== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCoxcz5kyyHsutS +sZLUrDrPh5NxxohMbEnZt0+k+t6vBAb/aWPk32SX9hQBTBh3+IKZzZmJnTF/XZHf +3IOJmvOf5BICuaAqldGgZK2V5NHDdmvkow70+EKsOs1M/Tlr3nLo+L4YWf/Ebvg/ +t2D0lVyYpnkNL0sqF4vNHzBtg1DbwESriWNUnkUJetKo7W+1Bn90rxOFnDrg4MWE +o0VNOymTpUB5ycgboAFTcT0RnTfzjbero5kREFeUdT3djwUgaXNoPjMoQaHNuYB0 +bX5n3GroNy5CSshTNSAdWWMikwYJfM17u2RdjDb2UBqmIgptwf0YEzqGDenXxJkG +xSZiSyWHAgMBAAECggEBAJi3/EXuVkIoteZrRulpPAJktRgkRVuaNa1rwgcQ94Y5 +R7fA/yKwvs7/XpTZ7d5JoqxYLlInSw3sdr6yRNi0vTV25JFQtU0zqq/Pyn+zwdh0 +hScsIbGsIHDG+NVs4pPog+SqiSFx9w4iOEM3xeUsbtC2+xziKcrZ6ahgS6sdQ4LE +3Wh6MfQTg36GWIhM4FgMiDVWOVV5lXjeRYtNWygfcr7dSDR4LTkUYFXOw/Y2r7gj +U/iWKGXv0VCb5fuzuDSyri2yfOFk73MxknSGseVF9ovUdxZVPaXGhKUt1687yB0o +k5vs6h1bHfutr/YyY8onp1k9teVMFdK1PTbwCKGcCokCgYEA0oF/pCd/5HPDbG6U +a1Yyl9pTWR/+BD7jy0cI39FM6hvNvGuoSaIJNjq1wlTA+HYPKD7WXdnInKQIZhhW +beNP3K+6lbMWa3vu3OlghlwbCymVH43mtWShsoJAACSsH+cjSJUKqxGRbebG2lJb +WFkCxPuGkOeMi3cY1mB+m6Aadz0CgYEAzT9cS5wSW1gTyrZAX7kgnQWdxttIIc53 +JnJXkNt9Re3WZ/d958hTO8GKeL5TRGUWP5B3depiRNedkqNq2Pb03/H7IdeIUnx0 +XDYy34Aq4LFuoss3gMBpQntxHi1ZsK8Dr0OhCmiXKRbOnG+DtRZnFtnWUMHsxT1T +GP97P3QKPBMCgYAt+GnPh3C+iylaPKvc5O4Xi244QGxQsQs+ET0245ZQadH8G+ac +be5lEV0wGyIpIF8fAtn9lNnim0/aO+ZkCQgfwaCVoI582ovHYo/Zx7OANIyEX5DK +fDdzrE4dueR88xUZfLLfuO5xebxjYSjhmk6oheVVL7vZmSCrcFc11TjyYQKBgEEj +TahVVOIWdtmLs6xtdXM/+A3HRAIuo1PZT/8SGZTa4YPHYPbxdbDuQIMHbCe1qTTt +JWyPwTY187U5Fczbu2VjR5865vG+u7MUJFsYMwNybDlQI0JCTmWiBYldPSy8rn+u +B+rkvKx2p4WwQm1zOa5f3gttMDZ8uuWhFz1RF3pvAoGBALEhLdvVbUVjWJzjQOep +hKdWXuo8hWd29XaxYrZkz0BatOxcPVK4PnaTH+dt31vhiE1EpYBFxb6ZsHu+g6NT +x1tsPWY+weGDpIdkvaQt9zrqLh7QaHpvu+S/2tV7DKpx9Qu+NlUsD/Vi12LnRxPU +0Kbv5wbAtjOuAoYMgcb19P1e +-----END PRIVATE KEY----- diff --git a/FDBLibTLS/testdata/test-server-4.pem b/FDBLibTLS/testdata/test-server-4.pem new file mode 100644 index 00000000000..24e0f7a0772 --- /dev/null +++ b/FDBLibTLS/testdata/test-server-4.pem @@ -0,0 +1,82 @@ +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Server 4 +issuer= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 3 +subject= C = US, ST = California, L = Cupertino, O = Apple Inc., OU = FDB Team, CN = FDB LibTLS Plugin Test Intermediate CA 3 +issuer= C = AU, ST = New South Wales, L = Sydney, O = Apple Pty Limited, OU = FDB Team, CN = FDB LibTLS Plugin Test Root CA 2 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIJALOPTrQGpet3MA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5v +MRMwEQYDVQQKDApBcHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTExMC8GA1UE +AwwoRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBJbnRlcm1lZGlhdGUgQ0EgMzAeFw0x +ODA0MjYxNjM1NTVaFw0yODA0MjMxNjM1NTVaMIGIMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMRMwEQYDVQQKDApB +cHBsZSBJbmMuMREwDwYDVQQLDAhGREIgVGVhbTEoMCYGA1UEAwwfRkRCIExpYlRM +UyBQbHVnaW4gVGVzdCBTZXJ2ZXIgNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANkcEPk77yl3JC11UYMR4IWtK4GZpZQ3QOMAD5UHM/naEOQ+tOcbU+C9 +WV+1AnPdmjdMgTuoPg4enJHJKqL/RcBdBfidOmlvgkXJsK22VAGX2vc2kl3KbM2O +8wdW0Wg1+1X6PsLl1agVuqA35BTtKliFGq2kWn/GD7g6Cr5cCcGCBh9SJo2WsCJB +S1NDeq/p0yST4C60pLpLC+nWerzclgZhtWE2rTQyPieAzMbK6BLI80fkyPnUjFLM +q8PZaZUHE3Vfb1GR5cQ2Xo9X433g68BzmPEkwrXi7Kz0clGQkihorlZcwKcBJtmw +sQr4Cj5THn939NDJLjrTTeBLsC1vhk0CAwEAAaNgMF4wHQYDVR0OBBYEFJEzltQT +5y5gQwKRibK7NAjfvJcfMB8GA1UdIwQYMBaAFNJkFNeVtJApFs//7Jvd5R/+BJ0Z +MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBBQUAA4IB +AQCD1/J06qb2mCcU2+nHKnSfuEg0lqyFiPm/MW+Mti0tqU49QtGzPTqECYIPgGgI +RbiWU/+lvTwShF6TnSynnvFAXpiqzogxgTD5AZAFaatDj07TZpeL3+15JZQRAA1c +JTGSiYbmEBR6H71aXVgUuIbiYp14Q63RX3OcOD65Yycn3jKOfUrHgfPn84+HDusW +2BscBu5fWmH0iNUxblVIihS5XRGoKnjcYAfBSuZ6T/0EOVEITV11Zd8cum97C9xO +a+ZJlwIPd8pM310DsulBC7EaQJNmlEaGrYHsBfjUZe93BsPfA/SK2iBDotuiSfoA +ZQslO1EMS1zORQb3LXDFZBuJ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIJALOPTrQGpet0MA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD +VQQGEwJBVTEYMBYGA1UECAwPTmV3IFNvdXRoIFdhbGVzMQ8wDQYDVQQHDAZTeWRu +ZXkxGjAYBgNVBAoMEUFwcGxlIFB0eSBMaW1pdGVkMREwDwYDVQQLDAhGREIgVGVh +bTEpMCcGA1UEAwwgRkRCIExpYlRMUyBQbHVnaW4gVGVzdCBSb290IENBIDIwHhcN +MTgwNDI2MTYzNTU0WhcNMjgwNDIzMTYzNTU0WjCBkTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzETMBEGA1UECgwK +QXBwbGUgSW5jLjERMA8GA1UECwwIRkRCIFRlYW0xMTAvBgNVBAMMKEZEQiBMaWJU +TFMgUGx1Z2luIFRlc3QgSW50ZXJtZWRpYXRlIENBIDMwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCZ3UPCarKiS+IqK2VvB0lVQ3L0TPuNUov54L7ELFnB +1lijZWX2wX3Vi0uAyvwmLi+PYkUkzhaNhGWbVPHEFqsOzwMbifGLfdxjetZD0kOr +ZuARithrDXVWZegIXEFDUdnpz74TKG6rCQ6o4WMIFUV/j9770iMtrMABr/BwASrE +sVqAqNe4bOqHJyxyeTxF/vCRme3PVf+u06mM9OLxVVaPRK+tiysdKprjs6oyFoxT +S/Cl+vv7Lnt5Yth0F7YJGMrIKDPuSWCqAfKWAc+b73XPQ14coIczVzQAikyRFmGa +9aaw0aWYI2gjTkuah0c5fboZAuHT0Z7nQVtYUyIYRY8BAgMBAAGjYzBhMB0GA1Ud +DgQWBBTSZBTXlbSQKRbP/+yb3eUf/gSdGTAfBgNVHSMEGDAWgBT8PGmqenESXKJl +kdMkGY7ttNqMZzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkq +hkiG9w0BAQUFAAOCAQEAdoyvb/2+r587RMar6+/OauAVXWwnNEI8hhUvxiTnDq/M +TKu+okSAHSbGvYp5yGD2IXlEZHjtKnMapsih7tvm2VboM7U5Zi3aK7hjvk6Kxs5B +eynHP9dDnUWG/qRbAF83B3mK7ymmrZvJIDwHZEtY9Gmmmb9ty9aK2ywmYtlYgIS8 +FSh/qwzvThIiMuCQma9JJOx6ZDXV2YwCxYtIRAuTvIWuOckb608fPi812k0zYILG +q4VLhZTc2AM8H/629RBRV5zC/tqySLb3VgpWQ2vZSzUwuKIdIPs4lsbNqmsdWIyR +hHnl8610t/JAUdCPHDd1bBlYNgLhMKJTM8fGu9Ux/Q== +-----END CERTIFICATE----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIx3nCjm2zsV8CAggA +MB0GCWCGSAFlAwQBAgQQK7Wa4+IkQwAbbEqowS8e8QSCBNAiqELNOXJ1yLnkuHku ++qMycK8XJQ/zqyQ2dikHzHX3i90OwHSdZp13i1uHe7nguq8Tz6NQqnEtcLevECz3 +EnEcAZK5Ipmonqn6QWZn6Lglh9mOv0Ykfdpiz2CKkJDwVzoWDPb/fOea0zlPNZAj +w8A+xsGA0Z2NkuBUpYFQgR6WtBKqndi0p6MRWG6aH21p3WCQ37jb0pzQTo3Ki53P +pq84JaphfSvj/DO7Cy0CfWHroAoODCxmjV8oPiddyLqk/Q3kv/nwHIqIKZN5s0OY +wSqkKJO4PkUqPPIa4QatKj7T6uZujRCEZIJPWfW/b9Om9CXdUpsvXEaZu41kAawC +jYyOqQ9CfCSC481R/LGtLOY/7vBiwUTRRLsrGeZkbmWMrK3spbsZqgTI5sl7Ya1C +tDOF7SUqauIdLLhK86jHwjLzebqv/qZ4W/zoOSFNlvsQCF4qRjLMlgA2JzOHtmAW +bYotgN+aOhQmxnrBJVBJChFE1vmsr+hpctJ0U62qtfD29yLO3q4xqbkg239lQEH1 +LtygB2mMJPtL3u+8K2ZNo2cZlNJFeDzNyPhB9+gT5Iil7OW897e9LJl5O/gCbgcS +IgDGY6vgLf64v5MjAbCTSmfX05biP9y1vgXqXT3YtXFqgCjsMZAKVibyXtq9DWUc +b6g1qX+RPBIGxHwCDQPoP/rdQF5WSWN/V6BYr2An9jLL2jRwQuvIG0hw8dtJWr45 +yGXP8A9QD6tL6rY9Bwzd0QjoH9HuQj9DwM6MRx0o2GG8lYJ5l70ZCHrHEO/3mKV0 +RvZBVCYm2wNRnrcmcfjdncMt2AF24Mk4CFcUgOUj8reK+Pg1+o5uThQwsPY7ttwq +rrm8/0Z9MIjPR/lKKunJU+SapULakhIsiSxuLP8R/G//wfjdZPO8T3AMlsyFglA+ +Tdq6b+ji2itL0foiUnaoJUVlH2aO6GteHxj8H4hfeF0IReYzfEiVjcew29sxY+MW +inLp0fcnAQqfk0rsA8agOIq/b/5PMpDvC1XM4MuRW7iLCydUTVENYYWNhIHffOy8 +fIQBTtqsHIEcytehZ6BjNIwr4JsdmcmUWiWveYIqdEv9ayIzuFQh5ZzAD/YemlXh +tCkKjoda+GKSdO5yUlZLRhkbmz/VSYXOLCYOo2nO0zNqyL/kMqQPjMNh6dxIKKTN +XXQjPUHDAjHWlBeXntM/Xqgl35QmzWb3gQU1xMEYqBDHkkc+lnhO/oSWBHaX43ov ++qnvcUaFGlDQexvFVO5AWujv4+Cx9eTukWjjMSKQ3RFsj8akPoKkl0Y+jONI4nR/ +WeSVSz7OFAT16Td8peVPnPCuz1bprmtBLJPSXTNgZNqDeudD9HnV2K0Rrg1D9rwy +NJyOERnw+9goVVqTcOTKON4FqqZM//aS2yrgnxLMjVb4bb69xgtBUjpoWHho9D1b +Aa88mrm8NsCIO8OxLrH9ZX4+CRpJa/SEmxPDQd/Z5qV1uMr7uRI3AlRkpqc3Ui8e +9qobzTd/VnIVMQK8JTW01/hwoK1mccEJPyF7phs22LpXZqKSU2oJ84NzfCLBvfyt +mipPVbuRblbmgRQiF0UhAzJO2JzeMbgY4M/zSXuet+YHYt0y1FrEUFh8dc9TXY8n +Rfsap1higlUefD6p/qj/E8KfVw== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/FDBLibTLS/verify-test.cpp b/FDBLibTLS/verify-test.cpp index d1754f7bb2c..43eee377bf3 100644 --- a/FDBLibTLS/verify-test.cpp +++ b/FDBLibTLS/verify-test.cpp @@ -22,7 +22,9 @@ #include #include -#include +#include + +#include #include "ITLSPlugin.h" #include "ReferenceCounted.h" @@ -32,10 +34,10 @@ struct FDBLibTLSVerifyTest { FDBLibTLSVerifyTest(std::string input): - input(input), valid(false), verify_cert(true), verify_time(true), subject_criteria({}), issuer_criteria({}) {}; - FDBLibTLSVerifyTest(std::string input, bool verify_cert, bool verify_time, std::map subject, std::map issuer): - input(input), valid(true), verify_cert(verify_cert), verify_time(verify_time), subject_criteria(subject), issuer_criteria(issuer) {}; - ~FDBLibTLSVerifyTest() {}; + input(input), valid(false), verify_cert(true), verify_time(true), subject_criteria({}), issuer_criteria({}), root_criteria({}) {}; + FDBLibTLSVerifyTest(std::string input, bool verify_cert, bool verify_time, std::map subject, std::map issuer, std::map root): + input(input), valid(true), verify_cert(verify_cert), verify_time(verify_time), subject_criteria(subject), issuer_criteria(issuer), root_criteria(root) {}; + ~FDBLibTLSVerifyTest() {}; int run(); @@ -47,6 +49,7 @@ struct FDBLibTLSVerifyTest { std::map subject_criteria; std::map issuer_criteria; + std::map root_criteria; }; static std::string printable( std::string const& val ) { @@ -80,35 +83,96 @@ static void logf(const char* event, void* uid, int is_error, ...) { } int FDBLibTLSVerifyTest::run() { - FDBLibTLSPlugin *plugin = new FDBLibTLSPlugin(); - FDBLibTLSPolicy *policy = new FDBLibTLSPolicy(Reference::addRef(plugin), (ITLSLogFunc)logf); - - bool rc = policy->set_verify_peers((const uint8_t *)input.c_str(), input.size()); - if (rc != valid) { + Reference verify; + try { + verify = Reference(new FDBLibTLSVerify(input)); + } catch ( const std::runtime_error& e ) { if (valid) { std::cerr << "FAIL: Verify test failed, but should have succeeded - '" << input << "'\n"; return 1; - } else { - std::cerr << "FAIL: Verify test should have failed, but succeeded - '" << input << "'\n"; - return 1; } + return 0; + } + if (!valid) { + std::cerr << "FAIL: Verify test should have failed, but succeeded - '" << input << "'\n"; + return 1; + } + if (verify->verify_cert != verify_cert) { + std::cerr << "FAIL: Got verify cert " << verify->verify_cert << ", want " << verify_cert << "\n"; + return 1; + } + if (verify->verify_time != verify_time) { + std::cerr << "FAIL: Got verify time " << verify->verify_time << ", want " << verify_time << "\n"; + return 1; + } + if (verify->subject_criteria != subject_criteria) { + std::cerr << "FAIL: Got subject criteria " << criteriaToString(verify->subject_criteria) << ", want " << criteriaToString(subject_criteria) << "\n"; + return 1; } - if (policy->verify_cert != verify_cert) { - std::cerr << "FAIL: Got verify cert " << policy->verify_cert << ", want " << verify_cert << "\n"; + if (verify->issuer_criteria != issuer_criteria) { + std::cerr << "FAIL: Got issuer criteria " << criteriaToString(verify->issuer_criteria) << ", want " << criteriaToString(issuer_criteria) << "\n"; return 1; } - if (policy->verify_time != verify_time) { - std::cerr << "FAIL: Got verify time " << policy->verify_time << ", want " << verify_time << "\n"; + if (verify->root_criteria != root_criteria) { + std::cerr << "FAIL: Got root criteria " << criteriaToString(verify->root_criteria) << ", want " << criteriaToString(root_criteria) << "\n"; return 1; } - if (policy->subject_criteria != subject_criteria) { - std::cerr << "FAIL: Got subject criteria " << criteriaToString(policy->subject_criteria) << ", want " << criteriaToString(subject_criteria) << "\n"; + return 0; +} + +static int policy_verify_test() { + Reference plugin = Reference(new FDBLibTLSPlugin()); + Reference policy = Reference(new FDBLibTLSPolicy(plugin, (ITLSLogFunc)logf)); + + const char *verify_peers[] = { + "S.CN=abc", + "I.CN=def", + "R.CN=xyz,Check.Unexpired=0", + }; + int verify_peers_len[] = { + (int)strlen(verify_peers[0]), + (int)strlen(verify_peers[1]), + (int)strlen(verify_peers[2]), + }; + Reference verify_rules[] = { + Reference(new FDBLibTLSVerify(std::string(verify_peers[0], verify_peers_len[0]))), + Reference(new FDBLibTLSVerify(std::string(verify_peers[1], verify_peers_len[1]))), + Reference(new FDBLibTLSVerify(std::string(verify_peers[2], verify_peers_len[2]))), + }; + + if (!policy->set_verify_peers(3, (const uint8_t **)verify_peers, verify_peers_len)) { + std::cerr << "FAIL: Policy verify test failed, but should have succeeded\n"; return 1; } - if (policy->issuer_criteria != issuer_criteria) { - std::cerr << "FAIL: Got issuer criteria " << criteriaToString(policy->issuer_criteria) << ", want " << criteriaToString(issuer_criteria) << "\n"; + if (policy->verify_rules.size() != 3) { + std::cerr << "FAIL: Got " << policy->verify_rules.size() << " verify rule, want 3\n"; return 1; } + + int i = 0; + for (auto &verify_rule: policy->verify_rules) { + if (verify_rule->verify_cert != verify_rules[i]->verify_cert) { + std::cerr << "FAIL: Got verify cert " << verify_rule->verify_cert << ", want " << verify_rules[i]->verify_cert << "\n"; + return 1; + } + if (verify_rule->verify_time != verify_rules[i]->verify_time) { + std::cerr << "FAIL: Got verify time " << verify_rule->verify_time << ", want " << verify_rules[i]->verify_time << "\n"; + return 1; + } + if (verify_rule->subject_criteria != verify_rules[i]->subject_criteria) { + std::cerr << "FAIL: Got subject criteria " << criteriaToString(verify_rule->subject_criteria) << ", want " << criteriaToString(verify_rules[i]->subject_criteria) << "\n"; + return 1; + } + if (verify_rule->issuer_criteria != verify_rules[i]->issuer_criteria) { + std::cerr << "FAIL: Got issuer criteria " << criteriaToString(verify_rule->issuer_criteria) << ", want " << criteriaToString(verify_rules[i]->issuer_criteria) << "\n"; + return 1; + } + if (verify_rule->root_criteria != verify_rules[i]->root_criteria) { + std::cerr << "FAIL: Got root criteria " << criteriaToString(verify_rule->root_criteria) << ", want " << criteriaToString(verify_rules[i]->root_criteria) << "\n"; + return 1; + } + i++; + } return 0; } @@ -117,27 +181,33 @@ int main(int argc, char **argv) int failed = 0; std::vector tests = { - FDBLibTLSVerifyTest("", true, true, {}, {}), - FDBLibTLSVerifyTest("Check.Valid=1", true, true, {}, {}), - FDBLibTLSVerifyTest("Check.Valid=0", false, true, {}, {}), - FDBLibTLSVerifyTest("Check.Unexpired=1", true, true, {}, {}), - FDBLibTLSVerifyTest("Check.Unexpired=0", true, false, {}, {}), - FDBLibTLSVerifyTest("Check.Valid=1,Check.Unexpired=0", true, false, {}, {}), - FDBLibTLSVerifyTest("Check.Unexpired=0,Check.Valid=0", false, false, {}, {}), + FDBLibTLSVerifyTest("", true, true, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Valid=1", true, true, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Valid=0", false, true, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Unexpired=1", true, true, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Unexpired=0", true, false, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Valid=1,Check.Unexpired=0", true, false, {}, {}, {}), + FDBLibTLSVerifyTest("Check.Unexpired=0,Check.Valid=0", false, false, {}, {}, {}), FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp\\, LLC", true, false, - {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}), + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}, {}), FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp\\= LLC", true, false, - {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp= LLC"}}, {{NID_countryName, "US"}}), + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp= LLC"}}, {{NID_countryName, "US"}}, {}), + FDBLibTLSVerifyTest("Check.Unexpired=0,R.C=US,C=US,S.O=XYZCorp\\= LLC", true, false, + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp= LLC"}}, {}, {{NID_countryName, "US"}}), FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp=LLC", true, false, - {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}), + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}, {}), FDBLibTLSVerifyTest("I.C=US,C=US,Check.Unexpired=0,S.O=XYZCorp=LLC", true, false, - {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}), + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}, {}), FDBLibTLSVerifyTest("I.C=US,C=US,S.O=XYZCorp\\, LLC", true, true, - {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}), - FDBLibTLSVerifyTest("C=\\,S=abc", true, true, {{NID_countryName, ",S=abc"}}, {}), - FDBLibTLSVerifyTest("CN=\\61\\62\\63", true, true, {{NID_commonName, "abc"}}, {}), - FDBLibTLSVerifyTest("CN=a\\62c", true, true, {{NID_commonName, "abc"}}, {}), - FDBLibTLSVerifyTest("CN=a\\01c", true, true, {{NID_commonName, "a\001c"}}, {}), + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}, {}), + FDBLibTLSVerifyTest("I.C=US,C=US,S.O=XYZCorp\\, LLC,R.CN=abc", true, true, + {{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, + {{NID_countryName, "US"}}, + {{NID_commonName, "abc"}}), + FDBLibTLSVerifyTest("C=\\,S=abc", true, true, {{NID_countryName, ",S=abc"}}, {}, {}), + FDBLibTLSVerifyTest("CN=\\61\\62\\63", true, true, {{NID_commonName, "abc"}}, {}, {}), + FDBLibTLSVerifyTest("CN=a\\62c", true, true, {{NID_commonName, "abc"}}, {}, {}), + FDBLibTLSVerifyTest("CN=a\\01c", true, true, {{NID_commonName, "a\001c"}}, {}, {}), // Invalid cases. FDBLibTLSVerifyTest("Check.Invalid=0"), @@ -153,5 +223,7 @@ int main(int argc, char **argv) for (auto &test: tests) failed |= test.run(); + failed |= policy_verify_test(); + return (failed); } diff --git a/bindings/bindingtester/bindingtester.py b/bindings/bindingtester/bindingtester.py index 983ff9c388e..5295cd27339 100755 --- a/bindings/bindingtester/bindingtester.py +++ b/bindings/bindingtester/bindingtester.py @@ -185,6 +185,8 @@ def __init__(self, args): # Test types should be intersection of all tester supported types self.args.types = reduce(lambda t1, t2: filter(t1.__contains__, t2), map(lambda tester: tester.types, self.testers)) + self.args.no_directory_snapshot_ops = self.args.no_directory_snapshot_ops or any([not tester.directory_snapshot_ops_enabled for tester in self.testers]) + def print_test(self): test_instructions = self._generate_test() @@ -424,6 +426,8 @@ def parse_args(argv): # SOMEDAY: this applies only to the scripted test. Should we invoke test files specifically (as in circus), # or invoke them here and allow tests to add arguments? parser.add_argument('--no-threads', action='store_true', help='Disables the START_THREAD instruction in the scripted test.') + + parser.add_argument('--no-directory-snapshot-ops', action='store_true', help='Disables snapshot operations for directory instructions.') return parser.parse_args(argv) diff --git a/bindings/bindingtester/known_testers.py b/bindings/bindingtester/known_testers.py index aebb0755e36..60d270bf89b 100644 --- a/bindings/bindingtester/known_testers.py +++ b/bindings/bindingtester/known_testers.py @@ -26,7 +26,7 @@ class Tester: - def __init__(self, name, cmd, max_int_bits=64, min_api_version=0, max_api_version=MAX_API_VERSION, threads_enabled=True, types=COMMON_TYPES): + def __init__(self, name, cmd, max_int_bits=64, min_api_version=0, max_api_version=MAX_API_VERSION, threads_enabled=True, types=COMMON_TYPES, directory_snapshot_ops_enabled=True): self.name = name self.cmd = cmd self.max_int_bits = max_int_bits @@ -34,6 +34,7 @@ def __init__(self, name, cmd, max_int_bits=64, min_api_version=0, max_api_versio self.max_api_version = max_api_version self.threads_enabled = threads_enabled self.types = types + self.directory_snapshot_ops_enabled = directory_snapshot_ops_enabled def supports_api_version(self, api_version): return api_version >= self.min_api_version and api_version <= self.max_api_version @@ -62,5 +63,5 @@ def _absolute_path(path): 'java': Tester('java', _java_cmd + 'StackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES), 'java_async': Tester('java', _java_cmd + 'AsyncStackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES), 'go': Tester('go', _absolute_path('go/build/bin/_stacktester'), 63, 200, MAX_API_VERSION), - 'flow': Tester('flow', _absolute_path('flow/bin/fdb_flow_tester'), 63, 500, MAX_API_VERSION), + 'flow': Tester('flow', _absolute_path('flow/bin/fdb_flow_tester'), 63, 500, MAX_API_VERSION, directory_snapshot_ops_enabled=False), } diff --git a/bindings/bindingtester/spec/bindingApiTester.txt b/bindings/bindingtester/spec/bindingApiTester.md similarity index 91% rename from bindings/bindingtester/spec/bindingApiTester.txt rename to bindings/bindingtester/spec/bindingApiTester.md index 398487c6635..6b6026aa3b7 100644 --- a/bindings/bindingtester/spec/bindingApiTester.txt +++ b/bindings/bindingtester/spec/bindingApiTester.md @@ -5,9 +5,9 @@ Your API test program must implement a simple stack machine that exercises the FoundationDB API. The program is invoked with two or three arguments. The first argument is a prefix that is the first element of a tuple, the second is the API version, and the third argument is the path to a cluster file. If the -third argument is not specified, your program may assume that fdb.open() will -succeed with no arguments (an fdb.cluster file will exist in the current -directory). Otherwise, your program should connect to the cluster specified +third argument is not specified, your program may assume that `fdb.open()` will +succeed with no arguments (an fdb.cluster file will exist in the current +directory). Otherwise, your program should connect to the cluster specified by the given cluster file. Your stack machine should begin reading the range returned by the tuple range @@ -38,7 +38,7 @@ instructions: and a peek operation. The stack is initialized to be empty. - A current FDB transaction name (stored as a byte string). The transaction - name should be initialized to the prefix that instructions are being read + name should be initialized to the prefix that instructions are being read from. - A last seen FDB version, which is a 64-bit integer. @@ -47,44 +47,44 @@ instructions: Data Operations --------------- -PUSH +#### PUSH <item> Pushes the provided item onto the stack. -DUP +#### DUP Duplicates the top item on the stack. The instruction number for the duplicate item should be the same as the original. -EMPTY_STACK +#### EMPTY_STACK Discards all items in the stack. -SWAP +#### SWAP Pops the top item off of the stack as INDEX. Swaps the items in the stack at depth 0 and depth INDEX. Does not modify the instruction numbers of the swapped items. -POP +#### POP Pops and discards the top item on the stack. -SUB +#### SUB Pops the top two items off of the stack as A and B and then pushes the difference (A-B) onto the stack. A and B may be assumed to be integers. -CONCAT +#### CONCAT - Pops the top two items off the stack as A and B and then pushes the + Pops the top two items off the stack as A and B and then pushes the concatenation of A and B onto the stack. A and B can be assumed to be of the same type and will be either byte strings or unicode strings. -LOG_STACK +#### LOG_STACK Pops the top item off the stack as PREFIX. Using a new transaction with normal - retry logic, inserts a key-value pair into the database for each item in the + retry logic, inserts a key-value pair into the database for each item in the stack of the form: PREFIX + tuple.pack((stackIndex, instructionNumber)) = tuple.pack((item,)) @@ -96,8 +96,8 @@ LOG_STACK then the value should be truncated to the first 40000 bytes of the packed tuple. - When finished, the stack should be empty. Note that because the stack may be - large, it may be necessary to commit the transaction every so often (e.g. + When finished, the stack should be empty. Note that because the stack may be + large, it may be necessary to commit the transaction every so often (e.g. after every 100 sets) to avoid past_version errors. FoundationDB Operations @@ -117,7 +117,7 @@ should simulate it using an anonymous transaction. Remember that set and clear operations must immediately commit (with appropriate retry behavior!). Any error that bubbles out of these operations must be caught. In the event of -an error, you must push the packed tuple of the string "ERROR" and the error +an error, you must push the packed tuple of the string `"ERROR"` and the error code (as a string, not an integer). Some operations may allow you to push future values onto the stack. When popping @@ -132,25 +132,25 @@ futures must apply the following rules to the result: - If the result is void (i.e. the future was just a signal of completion), then its value should be the byte string - "RESULT_NOT_PRESENT" + `"RESULT_NOT_PRESENT"` - If the result is from a GET operation in which no result was returned, then its value is to be converted to the byte string - "RESULT_NOT_PRESENT" + `"RESULT_NOT_PRESENT"` -NEW_TRANSACTION +#### NEW_TRANSACTION Creates a new transaction and stores it in the global transaction map under the currently used transaction name. -USE_TRANSACTION +#### USE_TRANSACTION Pop the top item off of the stack as TRANSACTION_NAME. Begin using the transaction stored at TRANSACTION_NAME in the transaction map for future operations. If no entry exists in the map for the given name, a new transaction should be inserted. -ON_ERROR +#### ON_ERROR Pops the top item off of the stack as ERROR_CODE. Passes ERROR_CODE in a language-appropriate way to the on_error method of current transaction @@ -158,22 +158,22 @@ ON_ERROR the error out as indicated above. May optionally push a future onto the stack. -GET (_SNAPSHOT, _DATABASE) +#### GET (_SNAPSHOT, _DATABASE) Pops the top item off of the stack as KEY and then looks up KEY in the database using the get() method. May optionally push a future onto the stack. -GET_KEY (_SNAPSHOT, _DATABASE) +#### GET_KEY (_SNAPSHOT, _DATABASE) Pops the top four items off of the stack as KEY, OR_EQUAL, OFFSET, PREFIX - and then constructs a key selector. This key selector is then resolved - using the get_key() method to yield RESULT. If RESULT starts with PREFIX, - then RESULT is pushed onto the stack. Otherwise, if RESULT < PREFIX, PREFIX + and then constructs a key selector. This key selector is then resolved + using the get_key() method to yield RESULT. If RESULT starts with PREFIX, + then RESULT is pushed onto the stack. Otherwise, if RESULT < PREFIX, PREFIX is pushed onto the stack. If RESULT > PREFIX, then strinc(PREFIX) is pushed onto the stack. May optionally push a future onto the stack. -GET_RANGE (_SNAPSHOT, _DATABASE) +#### GET_RANGE (_SNAPSHOT, _DATABASE) Pops the top five items off of the stack as BEGIN_KEY, END_KEY, LIMIT, REVERSE and STREAMING_MODE. Performs a range read in a language-appropriate @@ -181,103 +181,103 @@ GET_RANGE (_SNAPSHOT, _DATABASE) packed into a tuple as [k1,v1,k2,v2,...,kn,vn], and this single packed value is pushed onto the stack. -GET_RANGE_STARTS_WITH (_SNAPSHOT, _DATABASE) +#### GET_RANGE_STARTS_WITH (_SNAPSHOT, _DATABASE) Pops the top four items off of the stack as PREFIX, LIMIT, REVERSE and STREAMING_MODE. Performs a prefix range read in a language-appropriate way using these parameters. Output is pushed onto the stack as with GET_RANGE. -GET_RANGE_SELECTOR (_SNAPSHOT, _DATABASE) +#### GET_RANGE_SELECTOR (_SNAPSHOT, _DATABASE) Pops the top ten items off of the stack as BEGIN_KEY, BEGIN_OR_EQUAL, BEGIN_OFFSET, END_KEY, END_OR_EQUAL, END_OFFSET, LIMIT, REVERSE, - STREAMING_MODE, and PREFIX. Constructs key selectors BEGIN and END from + STREAMING_MODE, and PREFIX. Constructs key selectors BEGIN and END from the first six parameters, and then performs a range read in a language- - appropriate way using BEGIN, END, LIMIT, REVERSE and STREAMING_MODE. Output - is pushed onto the stack as with GET_RANGE, excluding any keys that do not + appropriate way using BEGIN, END, LIMIT, REVERSE and STREAMING_MODE. Output + is pushed onto the stack as with GET_RANGE, excluding any keys that do not begin with PREFIX. -GET_READ_VERSION (_SNAPSHOT) +#### GET_READ_VERSION (_SNAPSHOT) Gets the current read version and stores it in the internal stack machine state as the last seen version. Pushed the string "GOT_READ_VERSION" onto the stack. -GET_VERSIONSTAMP +#### GET_VERSIONSTAMP Calls get_versionstamp and pushes the resulting future onto the stack. -SET (_DATABASE) +#### SET (_DATABASE) Pops the top two items off of the stack as KEY and VALUE. Sets KEY to have the value VALUE. A SET_DATABASE call may optionally push a future onto the stack. -SET_READ_VERSION +#### SET_READ_VERSION Sets the current transaction read version to the internal state machine last seen version. -CLEAR (_DATABASE) +#### CLEAR (_DATABASE) Pops the top item off of the stack as KEY and then clears KEY from the database. A CLEAR_DATABASE call may optionally push a future onto the stack. -CLEAR_RANGE (_DATABASE) +#### CLEAR_RANGE (_DATABASE) Pops the top two items off of the stack as BEGIN_KEY and END_KEY. Clears the range of keys from BEGIN_KEY to END_KEY in the database. A CLEAR_RANGE_DATABASE call may optionally push a future onto the stack. -CLEAR_RANGE_STARTS_WITH (_DATABASE) +#### CLEAR_RANGE_STARTS_WITH (_DATABASE) Pops the top item off of the stack as PREFIX and then clears all keys from the database that begin with PREFIX. A CLEAR_RANGE_STARTS_WITH_DATABASE call may optionally push a future onto the stack. -ATOMIC_OP (_DATABASE) +#### ATOMIC_OP (_DATABASE) Pops the top three items off of the stack as OPTYPE, KEY, and VALUE. Performs the atomic operation described by OPTYPE upon KEY with VALUE. An ATOMIC_OP_DATABASE call may optionally push a future onto the stack. -READ_CONFLICT_RANGE and WRITE_CONFLICT_RANGE +#### READ_CONFLICT_RANGE and WRITE_CONFLICT_RANGE Pops the top two items off of the stack as BEGIN_KEY and END_KEY. Adds a read conflict range or write conflict range from BEGIN_KEY to END_KEY. Pushes the byte string "SET_CONFLICT_RANGE" onto the stack. -READ_CONFLICT_KEY and WRITE_CONFLICT_KEY +#### READ_CONFLICT_KEY and WRITE_CONFLICT_KEY Pops the top item off of the stack as KEY. Adds KEY as a read conflict key or write conflict key. Pushes the byte string "SET_CONFLICT_KEY" onto the stack. -DISABLE_WRITE_CONFLICT +#### DISABLE_WRITE_CONFLICT Sets the NEXT_WRITE_NO_WRITE_CONFLICT_RANGE transaction option on the current transaction. Does not modify the stack. -COMMIT +#### COMMIT Commits the current transaction (with no retry behavior). May optionally push a future onto the stack. -RESET +#### RESET Resets the current transaction. -CANCEL +#### CANCEL Cancels the current transaction. -GET_COMMITTED_VERSION +#### GET_COMMITTED_VERSION Gets the committed version from the current transaction and stores it in the internal stack machine state as the last seen version. Pushes the byte string "GOT_COMMITTED_VERSION" onto the stack. -WAIT_FUTURE +#### WAIT_FUTURE Pops the top item off the stack and pushes it back on. If the top item on the stack is a future, this will have the side effect of waiting on the @@ -287,13 +287,13 @@ WAIT_FUTURE Tuple Operations ---------------- -TUPLE_PACK +#### TUPLE_PACK Pops the top item off of the stack as N. Pops the next N items off of the stack and packs them as the tuple [item0,item1,...,itemN], and then pushes this single packed value onto the stack. -TUPLE_PACK_WITH_VERSIONSTAMP +#### TUPLE_PACK_WITH_VERSIONSTAMP Pops the top item off of the stack as a byte string prefix. Pops the next item off of the stack as N. Pops the next N items off of the stack and packs them @@ -307,20 +307,20 @@ TUPLE_PACK_WITH_VERSIONSTAMP do not contain a 'Versionstamp' tuple-type do not have to implement this operation.) -TUPLE_UNPACK +#### TUPLE_UNPACK Pops the top item off of the stack as PACKED, and then unpacks PACKED into a tuple. For each element of the tuple, packs it as a new tuple and pushes it onto the stack. -TUPLE_RANGE +#### TUPLE_RANGE Pops the top item off of the stack as N. Pops the next N items off of the stack, and passes these items as a tuple (or array, or language-appropriate structure) to the tuple range method. Pushes the begin and end elements of the returned range onto the stack. -TUPLE_SORT +#### TUPLE_SORT Pops the top item off of the stack as N. Pops the next N items off of the stack as packed tuples (i.e., byte strings), unpacks them, sorts the tuples, @@ -330,25 +330,25 @@ TUPLE_SORT use that to sort. Otherwise, it should sort them lexicographically by their byte representation. The choice of function should not affect final sort order. -ENCODE_FLOAT +#### ENCODE_FLOAT Pops the top item off of the stack. This will be a byte-string of length 4 containing the IEEE 754 encoding of a float in big-endian order. This is then converted into a float and pushed onto the stack. -ENCODE_DOUBLE +#### ENCODE_DOUBLE Pops the top item off of the stack. This will be a byte-string of length 8 containing the IEEE 754 encoding of a double in big-endian order. This is then converted into a double and pushed onto the stack. -DECODE_FLOAT +#### DECODE_FLOAT Pops the top item off of the stack. This will be a single-precision float. This is converted into a (4 byte) byte-string of its IEEE 754 representation in big-endian order, and pushed onto the stack. -DECODE_DOUBLE +#### DECODE_DOUBLE Pops the top item off of the stack. This will be a double-precision float. This is converted into a (8 byte) byte-string its IEEE 754 representation @@ -358,7 +358,7 @@ DECODE_DOUBLE Thread Operations ----------------- -START_THREAD +#### START_THREAD Pops the top item off of the stack as PREFIX. Creates a new stack machine instance operating on the same database as the current stack machine, but @@ -366,7 +366,7 @@ START_THREAD state. The new stack machine should begin executing instructions concurrent with the current stack machine through a language-appropriate mechanism. -WAIT_EMPTY +#### WAIT_EMPTY Pops the top item off of the stack as PREFIX. Blocks execution until the range with prefix PREFIX is not present in the database. This should be @@ -378,7 +378,7 @@ WAIT_EMPTY Miscellaneous ------------- -UNIT_TESTS +#### UNIT_TESTS This is called during the scripted test to allow bindings to test features which aren't supported by the stack tester. Things currently tested in the diff --git a/bindings/bindingtester/spec/directoryLayerTester.txt b/bindings/bindingtester/spec/directoryLayerTester.md similarity index 80% rename from bindings/bindingtester/spec/directoryLayerTester.txt rename to bindings/bindingtester/spec/directoryLayerTester.md index 03ed3135d20..18489b69a21 100644 --- a/bindings/bindingtester/spec/directoryLayerTester.txt +++ b/bindings/bindingtester/spec/directoryLayerTester.md @@ -8,19 +8,19 @@ directory testing state. Additional State and Initialization ----------------------------------- -Your tester should store three additional pieces of state. +Your tester should store three additional pieces of state. -directory list - The items in this list should be accessible by index. The list -should support an append operation. It will be required to store Subspaces, +* directory list - The items in this list should be accessible by index. The list +should support an append operation. It will be required to store Subspaces, DirectorySubspaces, and DirectoryLayers. -directory list index - an index into the directory list of the currently active +* directory list index - an index into the directory list of the currently active directory. -error index - the index to use when the directory at directory list index is not +* error index - the index to use when the directory at directory list index is not present -At the beginning of the test, the list should contain just the default directory +At the beginning of the test, the list should contain just the default directory layer. The directory index and error index should both be set to 0. Popping Tuples @@ -29,14 +29,14 @@ Popping Tuples Some instructions will require you to pop N tuples. To do this, repeat the following procedure N times: -Pop 1 item off the stack as M. Pop M items off the stack as +Pop 1 item off the stack as M. Pop M items off the stack as tuple = [item1, ..., itemM]. Errors ------ In the even that you encounter an error when performing a directory layer -operation, you should push the byte string: "DIRECTORY_ERROR" onto the stack. If +operation, you should push the byte string: `"DIRECTORY_ERROR"` onto the stack. If the operation being performed was supposed to append an item to the directory list, then a null entry should be appended instead. @@ -46,65 +46,65 @@ New Instructions Below are the new instructions that must be implemented to test the directory layer. Some instructions specify that the current directory should be used for the operation. In that case, use the object in the directory list specified -by the current directory list index. Operations that are not defined for a +by the current directory list index. Operations that are not defined for a particular object will not be called (e.g. a DirectoryLayer will never be asked to pack a key). Directory/Subspace/Layer Creation --------------------------------- -DIRECTORY_CREATE_SUBSPACE +#### DIRECTORY_CREATE_SUBSPACE Pop 1 tuple off the stack as [path]. Pop 1 additional item as [raw_prefix]. - Create a subspace with path as the prefix tuple and the specified + Create a subspace with path as the prefix tuple and the specified raw_prefix. Append it to the directory list. -DIRECTORY_CREATE_LAYER +#### DIRECTORY_CREATE_LAYER - Pop 3 items off the stack as [index1, index2, allow_manual_prefixes]. Let - node_subspace be the object in the directory list at index1 and - content_subspace be the object in the directory list at index2. Create a new - directory layer with the specified node_subspace and content_subspace. If + Pop 3 items off the stack as [index1, index2, allow_manual_prefixes]. Let + node_subspace be the object in the directory list at index1 and + content_subspace be the object in the directory list at index2. Create a new + directory layer with the specified node_subspace and content_subspace. If allow_manual_prefixes is 1, then enable manual prefixes on the directory layer. Append the resulting directory layer to the directory list. - If either of the two specified subspaces are null, then do not create a + If either of the two specified subspaces are null, then do not create a directory layer and instead push null onto the directory list. -DIRECTORY_CREATE_OR_OPEN[_DATABASE] +#### DIRECTORY_CREATE_OR_OPEN[_DATABASE] Use the current directory for this operation. - Pop 1 tuple off the stack as [path]. Pop 1 additional item as [layer]. - create_or_open a directory with the specified path and layer. If layer is + Pop 1 tuple off the stack as [path]. Pop 1 additional item as [layer]. + create_or_open a directory with the specified path and layer. If layer is null, use the default value for that parameter. -DIRECTORY_CREATE[_DATABASE] +#### DIRECTORY_CREATE[_DATABASE] - Pop 1 tuple off the stack as [path]. Pop 2 additional items as - [layer, prefix]. create a directory with the specified path, layer, - and prefix. If either of layer or prefix is null, use the default value for + Pop 1 tuple off the stack as [path]. Pop 2 additional items as + [layer, prefix]. create a directory with the specified path, layer, + and prefix. If either of layer or prefix is null, use the default value for that parameter (layer='', prefix=null). -DIRECTORY_OPEN[_DATABASE|_SNAPSHOT] +#### DIRECTORY_OPEN[_DATABASE|_SNAPSHOT] Use the current directory for this operation. - Pop 1 tuple off the stack as [path]. Pop 1 additional item as [layer]. Open - a directory with the specified path and layer. If layer is null, use the + Pop 1 tuple off the stack as [path]. Pop 1 additional item as [layer]. Open + a directory with the specified path and layer. If layer is null, use the default value (layer=''). Directory Management -------------------- -DIRECTORY_CHANGE +#### DIRECTORY_CHANGE Pop the top item off the stack as [index]. Set the current directory list index to index. In the event that the directory at this new index is null - (as the result of a previous error), set the directory list index to the + (as the result of a previous error), set the directory list index to the error index. -DIRECTORY_SET_ERROR_INDEX +#### DIRECTORY_SET_ERROR_INDEX Pop the top item off the stack as [error_index]. Set the current error index to error_index. @@ -112,130 +112,130 @@ DIRECTORY_SET_ERROR_INDEX Directory Operations -------------------- -DIRECTORY_MOVE[_DATABASE] +#### DIRECTORY_MOVE[_DATABASE] Use the current directory for this operation. - Pop 2 tuples off the stack as [old_path, new_path]. Call move with the + Pop 2 tuples off the stack as [old_path, new_path]. Call move with the specified old_path and new_path. Append the result onto the directory list. -DIRECTORY_MOVE_TO[_DATABASE] +#### DIRECTORY_MOVE_TO[_DATABASE] Use the current directory for this operation. - Pop 1 tuple off the stack as [new_absolute_path]. Call moveTo with the + Pop 1 tuple off the stack as [new_absolute_path]. Call moveTo with the specified new_absolute_path. Append the result onto the directory list. -DIRECTORY_REMOVE[_DATABASE] +#### DIRECTORY_REMOVE[_DATABASE] Use the current directory for this operation. Pop 1 item off the stack as [count] (either 0 or 1). If count is 1, pop 1 - tuple off the stack as [path]. Call remove, passing it path if one was + tuple off the stack as [path]. Call remove, passing it path if one was popped. -DIRECTORY_REMOVE_IF_EXISTS[_DATABASE] +#### DIRECTORY_REMOVE_IF_EXISTS[_DATABASE] Use the current directory for this operation. Pop 1 item off the stack as [count] (either 0 or 1). If count is 1, pop 1 - tuple off the stack as [path]. Call remove_if_exits, passing it path if one + tuple off the stack as [path]. Call remove_if_exits, passing it path if one was popped. -DIRECTORY_LIST[_DATABASE|_SNAPSHOT] +#### DIRECTORY_LIST[_DATABASE|_SNAPSHOT] Use the current directory for this operation. Pop 1 item off the stack as [count] (either 0 or 1). If count is 1, pop 1 - tuple off the stack as [path]. Call list, passing it path if one was popped. - Pack the resulting list of directories using the tuple layer and push the + tuple off the stack as [path]. Call list, passing it path if one was popped. + Pack the resulting list of directories using the tuple layer and push the packed string onto the stack. -DIRECTORY_EXISTS[_DATABASE|_SNAPSHOT] +#### DIRECTORY_EXISTS[_DATABASE|_SNAPSHOT] Use the current directory for this operation. Pop 1 item off the stack as [count] (either 0 or 1). If count is 1, pop 1 - tuple off the stack as [path]. Call exists, passing it path if one + tuple off the stack as [path]. Call exists, passing it path if one was popped. Push 1 onto the stack if the path exists and 0 if it does not. Subspace Operations ------------------- -DIRECTORY_PACK_KEY +#### DIRECTORY_PACK_KEY Use the current directory for this operation. Pop 1 tuple off the stack as [key_tuple]. Pack key_tuple and push the result onto the stack. -DIRECTORY_UNPACK_KEY +#### DIRECTORY_UNPACK_KEY Use the current directory for this operation. Pop 1 item off the stack as [key]. Unpack key and push the resulting tuple onto the stack one item at a time. -DIRECTORY_RANGE +#### DIRECTORY_RANGE Use the current directory for this operation. - Pop 1 tuple off the stack as [tuple]. Create a range using tuple and push + Pop 1 tuple off the stack as [tuple]. Create a range using tuple and push range.begin and range.end onto the stack. -DIRECTORY_CONTAINS +#### DIRECTORY_CONTAINS Use the current directory for this operation. Pop 1 item off the stack as [key]. Check if the current directory contains the specified key. Push 1 if it does and 0 if it doesn't. -DIRECTORY_OPEN_SUBSPACE +#### DIRECTORY_OPEN_SUBSPACE Use the current directory for this operation. - Pop 1 tuple off the stack as [tuple]. Open the subspace of the current + Pop 1 tuple off the stack as [tuple]. Open the subspace of the current directory specified by tuple and push it onto the directory list. Directory Logging -------------------- -DIRECTORY_LOG_SUBSPACE +#### DIRECTORY_LOG_SUBSPACE Use the current directory for this operation. - Pop 1 item off the stack as [prefix]. Let key equal + Pop 1 item off the stack as [prefix]. Let key equal prefix + tuple.pack([dir_index]). Set key to be the result of calling - directory.key() in the current transaction. + directory.key() in the current transaction. -DIRECTORY_LOG_DIRECTORY +#### DIRECTORY_LOG_DIRECTORY Use the current directory for this operation. - Pop 1 item off the stack as [raw_prefix]. Create a subspace log_subspace + Pop 1 item off the stack as [raw_prefix]. Create a subspace log_subspace with path (dir_index) and the specified raw_prefix. Set: tr[log_subspace[u'path']] = the tuple packed path of the directory. tr[log_subspace[u'layer']] = the tuple packed layer of the directory. - tr[log_subspace[u'exists']] = the packed tuple containing a 1 if the + tr[log_subspace[u'exists']] = the packed tuple containing a 1 if the directory exists and 0 if it doesn't. - tr[log_subspace[u'children']] the tuple packed list of children of the + tr[log_subspace[u'children']] the tuple packed list of children of the directory. - Where log_subspace[u] is the subspace packed tuple containing only the + Where log_subspace[u] is the subspace packed tuple containing only the single specified unicode string . Other ----- -DIRECTORY_STRIP_PREFIX +#### DIRECTORY_STRIP_PREFIX Use the current directory for this operation. - Pop 1 item off the stack as [byte_array]. Call .key() on the current - subspace and store the result as [prefix]. Throw an error if the popped - array does not start with prefix. Otherwise, remove the prefix from the + Pop 1 item off the stack as [byte_array]. Call .key() on the current + subspace and store the result as [prefix]. Throw an error if the popped + array does not start with prefix. Otherwise, remove the prefix from the popped array and push the result onto the stack. diff --git a/bindings/bindingtester/tests/__init__.py b/bindings/bindingtester/tests/__init__.py index 20f45835e6d..89835affaed 100644 --- a/bindings/bindingtester/tests/__init__.py +++ b/bindings/bindingtester/tests/__init__.py @@ -20,6 +20,7 @@ import math import re +import struct import fdb @@ -80,6 +81,20 @@ def get_expected_results(self): def validate(self, db, args): return [] + def versionstamp_key(self, raw_bytes, version_pos): + if hasattr(self, 'api_version') and self.api_version < 520: + return raw_bytes + struct.pack('= 520: + instructions.push_args(u'SET_VERSIONSTAMPED_VALUE', key2, self.versionstamp_value(rand_str2, len(prefix))) + instructions.append('ATOMIC_OP') + + instructions.push_args(u'SET_VERSIONSTAMPED_KEY', key3, rand_str1) instructions.append('ATOMIC_OP') self.can_use_key_selectors = False @@ -430,16 +445,17 @@ def generate(self, args, thread_number): elif op == 'TUPLE_PACK_WITH_VERSIONSTAMP': tup = (self.random.random_string(20),) + self.random.random_tuple(10, incomplete_versionstamps=True) - instructions.push_args(self.versionstamped_keys.pack(), len(tup), *tup) + prefix = self.versionstamped_keys.pack() + instructions.push_args(prefix, len(tup), *tup) instructions.append(op) self.add_strings(1) - version_key = self.versionstamped_keys.pack(tup) - first_incomplete = version_key.find(fdb.tuple.Versionstamp._UNSET_TR_VERSION) + versionstamp_param = prefix + fdb.tuple.pack(tup) + first_incomplete = versionstamp_param.find(fdb.tuple.Versionstamp._UNSET_TR_VERSION) second_incomplete = -1 if first_incomplete < 0 else \ - version_key.find(fdb.tuple.Versionstamp._UNSET_TR_VERSION, first_incomplete + len(fdb.tuple.Versionstamp._UNSET_TR_VERSION) + 1) + versionstamp_param.find(fdb.tuple.Versionstamp._UNSET_TR_VERSION, first_incomplete + len(fdb.tuple.Versionstamp._UNSET_TR_VERSION) + 1) - # If there is exactly one incomplete versionstamp, perform the versionstamped key operation. + # If there is exactly one incomplete versionstamp, perform the versionstamp operation. if first_incomplete >= 0 and second_incomplete < 0: rand_str = self.random.random_string(100) @@ -448,9 +464,15 @@ def generate(self, args, thread_number): instructions.push_args(u'SET_VERSIONSTAMPED_KEY') instructions.append('ATOMIC_OP') + if self.api_version >= 520: + version_value_key_2 = self.versionstamped_values_2.pack((rand_str,)) + versionstamped_value = self.versionstamp_value(fdb.tuple.pack(tup), first_incomplete - len(prefix)) + instructions.push_args(u'SET_VERSIONSTAMPED_VALUE', version_value_key_2, versionstamped_value) + instructions.append('ATOMIC_OP') + version_value_key = self.versionstamped_values.pack((rand_str,)) instructions.push_args(u'SET_VERSIONSTAMPED_VALUE', version_value_key, - fdb.tuple.Versionstamp._UNSET_TR_VERSION + fdb.tuple.pack(tup)) + self.versionstamp_value(fdb.tuple.Versionstamp._UNSET_TR_VERSION + fdb.tuple.pack(tup))) instructions.append('ATOMIC_OP') self.can_use_key_selectors = False @@ -510,7 +532,7 @@ def generate(self, args, thread_number): self.add_strings(1) else: - assert False + assert False, 'Unknown operation: ' + op if read_performed and op not in database_reads: self.outstanding_ops.append((self.stack_size, len(instructions) - 1)) @@ -539,13 +561,22 @@ def check_versionstamps(self, tr, begin_key, limit): incorrect_versionstamps = 0 for k, v in tr.get_range(begin_key, self.versionstamped_values.range().stop, limit=limit): next_begin = k + '\x00' - tup = fdb.tuple.unpack(k) - key = self.versionstamped_keys.pack() + v[10:].replace(fdb.tuple.Versionstamp._UNSET_TR_VERSION, v[:10], 1) - if tr[key] != tup[-1]: + random_id = self.versionstamped_values.unpack(k)[0] + versioned_value = v[10:].replace(fdb.tuple.Versionstamp._UNSET_TR_VERSION, v[:10], 1) + + versioned_key = self.versionstamped_keys.pack() + versioned_value + if tr[versioned_key] != random_id: util.get_logger().error(' INCORRECT VERSIONSTAMP:') - util.get_logger().error(' %s != %s', repr(tr[key]), repr(tup[-1])) + util.get_logger().error(' %s != %s', repr(tr[versioned_key]), repr(random_id)) incorrect_versionstamps += 1 + if self.api_version >= 520: + k2 = self.versionstamped_values_2.pack((random_id,)) + if tr[k2] != versioned_value: + util.get_logger().error(' INCORRECT VERSIONSTAMP:') + util.get_logger().error(' %s != %s', repr(tr[k2]), repr(versioned_value)) + incorrect_versionstamps += 1 + return (next_begin, incorrect_versionstamps) def validate(self, db, args): diff --git a/bindings/bindingtester/tests/directory.py b/bindings/bindingtester/tests/directory.py index 861ca939727..e675c03cb66 100644 --- a/bindings/bindingtester/tests/directory.py +++ b/bindings/bindingtester/tests/directory.py @@ -28,7 +28,7 @@ from bindingtester.tests import Test, Instruction, InstructionSet, ResultSpecification from bindingtester.tests import test_util, directory_util -from bindingtester.tests.directory_util import DirListEntry +from bindingtester.tests.directory_state_tree import DirectoryStateTreeNode fdb.api_version(FDB_API_VERSION) @@ -48,12 +48,12 @@ def __init__(self, subspace): def ensure_default_directory_subspace(self, instructions, path): directory_util.create_default_directory_subspace(instructions, path, self.random) - child = self.root.add_child((path,), path, self.root, DirListEntry(True, True)) + child = self.root.add_child(path, DirectoryStateTreeNode(True, True, has_known_prefix=True)) self.dir_list.append(child) self.dir_index = directory_util.DEFAULT_DIRECTORY_INDEX def generate_layer(self): - if random.random < 0.7: + if random.random() < 0.7: return '' else: choice = random.randint(0, 3) @@ -90,7 +90,9 @@ def generate(self, args, thread_number): directory += directory_reads directory += directory_db_mutations directory += directory_db_reads - directory += directory_snapshot_reads + + if not args.no_directory_snapshot_ops: + directory += directory_snapshot_reads subspace = ['DIRECTORY_PACK_KEY', 'DIRECTORY_UNPACK_KEY', 'DIRECTORY_RANGE', 'DIRECTORY_CONTAINS', 'DIRECTORY_OPEN_SUBSPACE'] @@ -112,29 +114,34 @@ def generate(self, args, thread_number): instructions.push_args(layer) instructions.push_args(*test_util.with_length(path)) instructions.append('DIRECTORY_OPEN') - # print '%d. Selected %s, dir=%s, has_known_prefix=%s, dir_list_len=%d' \ - # % (len(instructions), 'DIRECTORY_OPEN', repr(self.dir_index), False, len(self.dir_list)) - self.dir_list.append(self.dir_list[0].add_child(path, default_path, self.root, DirListEntry(True, True, has_known_prefix=False))) + self.dir_list.append(self.root.add_child(path, DirectoryStateTreeNode(True, True, has_known_prefix=False))) + # print('%d. Selected %s, dir=%s, dir_id=%s, has_known_prefix=%s, dir_list_len=%d' \ + # % (len(instructions), 'DIRECTORY_OPEN', repr(self.dir_index), self.dir_list[-1].dir_id, False, len(self.dir_list)-1)) instructions.setup_complete() for i in range(args.num_ops): if random.random() < 0.5: - self.dir_index = random.randrange(0, len(self.dir_list)) + while True: + self.dir_index = random.randrange(0, len(self.dir_list)) + if not self.dir_list[self.dir_index].state.is_partition or not self.dir_list[self.dir_index].state.deleted: + break + instructions.push_args(self.dir_index) instructions.append('DIRECTORY_CHANGE') + dir_entry = self.dir_list[self.dir_index] + choices = op_choices[:] - if self.dir_list[self.dir_index].is_directory: + if dir_entry.state.is_directory: choices += directory - if self.dir_list[self.dir_index].is_subspace: + if dir_entry.state.is_subspace: choices += subspace op = random.choice(choices) - dir_entry = self.dir_list[self.dir_index] - # print '%d. Selected %s, dir=%s, has_known_prefix=%s, dir_list_len=%d' \ - # % (len(instructions), op, repr(self.dir_index), repr(dir_entry.has_known_prefix), len(self.dir_list)) + # print('%d. Selected %s, dir=%d, dir_id=%d, has_known_prefix=%d, dir_list_len=%d' \ + # % (len(instructions), op, self.dir_index, dir_entry.dir_id, dir_entry.state.has_known_prefix, len(self.dir_list))) if op.endswith('_DATABASE') or op.endswith('_SNAPSHOT'): root_op = op[0:-9] @@ -149,24 +156,26 @@ def generate(self, args, thread_number): elif root_op == 'DIRECTORY_CREATE_SUBSPACE': path = generate_path() - instructions.push_args(generate_prefix(allow_empty=False, is_partition=True)) + instructions.push_args(generate_prefix(require_unique=False, is_partition=True)) instructions.push_args(*test_util.with_length(path)) instructions.append(op) - self.dir_list.append(DirListEntry(False, True)) + self.dir_list.append(DirectoryStateTreeNode(False, True, has_known_prefix=True)) elif root_op == 'DIRECTORY_CREATE_LAYER': indices = [] + + prefixes = [generate_prefix(require_unique=args.concurrency==1, is_partition=True) for i in range(2)] for i in range(2): - instructions.push_args(generate_prefix(allow_empty=False, is_partition=True)) + instructions.push_args(prefixes[i]) instructions.push_args(*test_util.with_length(generate_path())) instructions.append('DIRECTORY_CREATE_SUBSPACE') indices.append(len(self.dir_list)) - self.dir_list.append(DirListEntry(False, True)) + self.dir_list.append(DirectoryStateTreeNode(False, True, has_known_prefix=True)) instructions.push_args(random.choice([0, 1])) instructions.push_args(*indices) instructions.append(op) - self.dir_list.append(DirListEntry(True, False, False)) + self.dir_list.append(DirectoryStateTreeNode.get_layer(prefixes[0])) elif root_op == 'DIRECTORY_CREATE_OR_OPEN': # Because allocated prefixes are non-deterministic, we cannot have overlapping @@ -181,14 +190,18 @@ def generate(self, args, thread_number): if not op.endswith('_DATABASE') and args.concurrency == 1: test_util.blocking_commit(instructions) - self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True, False))) + child_entry = dir_entry.get_descendent(path) + if child_entry is None: + child_entry = DirectoryStateTreeNode(True, True) + + child_entry.state.has_known_prefix = False + self.dir_list.append(dir_entry.add_child(path, child_entry)) elif root_op == 'DIRECTORY_CREATE': layer = self.generate_layer() is_partition = layer == 'partition' - allow_empty_prefix = random.random() < 0.8 - prefix = generate_prefix(allow_empty=allow_empty_prefix, is_partition=is_partition) + prefix = generate_prefix(require_unique=is_partition and args.concurrency==1, is_partition=is_partition, min_length=0) # Because allocated prefixes are non-deterministic, we cannot have overlapping # transactions that allocate/remove these prefixes in a comparison test @@ -207,40 +220,59 @@ def generate(self, args, thread_number): if not op.endswith('_DATABASE') and args.concurrency == 1: # and allow_empty_prefix: test_util.blocking_commit(instructions) - self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True, bool(prefix)))) + child_entry = dir_entry.get_descendent(path) + if child_entry is None: + child_entry = DirectoryStateTreeNode(True, True, has_known_prefix=bool(prefix)) + elif not bool(prefix): + child_entry.state.has_known_prefix = False + + if is_partition: + child_entry.state.is_partition = True + + self.dir_list.append(dir_entry.add_child(path, child_entry)) elif root_op == 'DIRECTORY_OPEN': path = generate_path() instructions.push_args(self.generate_layer()) instructions.push_args(*test_util.with_length(path)) instructions.append(op) - self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True))) + + child_entry = dir_entry.get_descendent(path) + if child_entry is None: + self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False)) + else: + self.dir_list.append(dir_entry.add_child(path, child_entry)) elif root_op == 'DIRECTORY_MOVE': old_path = generate_path() new_path = generate_path() instructions.push_args(*(test_util.with_length(old_path) + test_util.with_length(new_path))) instructions.append(op) - # This could probably be made to sometimes set has_known_prefix to true - self.dir_list.append(dir_entry.add_child(new_path, default_path, self.root, DirListEntry(True, True, False))) + + child_entry = dir_entry.get_descendent(old_path) + if child_entry is None: + self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False)) + else: + self.dir_list.append(dir_entry.add_child(new_path, child_entry)) # Make sure that the default directory subspace still exists after moving the specified directory - if dir_entry.is_directory and not dir_entry.is_subspace and old_path == (u'',): + if dir_entry.state.is_directory and not dir_entry.state.is_subspace and old_path == (u'',): self.ensure_default_directory_subspace(instructions, default_path) elif root_op == 'DIRECTORY_MOVE_TO': new_path = generate_path() instructions.push_args(*test_util.with_length(new_path)) instructions.append(op) - self.dir_list.append(dir_entry.root.add_child(new_path, default_path, self.root, - DirListEntry(True, True, dir_entry.has_known_prefix))) + + child_entry = dir_entry.get_descendent(()) + if child_entry is None: + self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False)) + else: + self.dir_list.append(dir_entry.add_child(new_path, child_entry)) # Make sure that the default directory subspace still exists after moving the current directory self.ensure_default_directory_subspace(instructions, default_path) - # FIXME: There is currently a problem with removing partitions. In these generated tests, it's possible - # for a removed partition to resurrect itself and insert keys into the database using its allocated - # prefix. The result is non-deterministic HCA errors. elif root_op == 'DIRECTORY_REMOVE' or root_op == 'DIRECTORY_REMOVE_IF_EXISTS': # Because allocated prefixes are non-deterministic, we cannot have overlapping # transactions that allocate/remove these prefixes in a comparison test @@ -252,12 +284,14 @@ def generate(self, args, thread_number): if count == 1: path = generate_path() instructions.push_args(*test_util.with_length(path)) - instructions.push_args(count) + instructions.push_args(count) instructions.append(op) + dir_entry.delete(path) + # Make sure that the default directory subspace still exists after removing the specified directory - if path == () or (dir_entry.is_directory and not dir_entry.is_subspace and path == (u'',)): + if path == () or (dir_entry.state.is_directory and not dir_entry.state.is_subspace and path == (u'',)): self.ensure_default_directory_subspace(instructions, default_path) elif root_op == 'DIRECTORY_LIST' or root_op == 'DIRECTORY_EXISTS': @@ -276,7 +310,7 @@ def generate(self, args, thread_number): instructions.append('DIRECTORY_STRIP_PREFIX') elif root_op == 'DIRECTORY_UNPACK_KEY' or root_op == 'DIRECTORY_CONTAINS': - if not dir_entry.has_known_prefix or random.random() < 0.2 or root_op == 'DIRECTORY_UNPACK_KEY': + if not dir_entry.state.has_known_prefix or random.random() < 0.2 or root_op == 'DIRECTORY_UNPACK_KEY': t = self.random.random_tuple(5) instructions.push_args(*test_util.with_length(t)) instructions.append('DIRECTORY_PACK_KEY') @@ -290,7 +324,7 @@ def generate(self, args, thread_number): instructions.push_args(*test_util.with_length(t)) instructions.append(op) if root_op == 'DIRECTORY_OPEN_SUBSPACE': - self.dir_list.append(DirListEntry(False, True, dir_entry.has_known_prefix)) + self.dir_list.append(DirectoryStateTreeNode(False, True, dir_entry.state.has_known_prefix)) else: test_util.to_front(instructions, 1) instructions.append('DIRECTORY_STRIP_PREFIX') @@ -306,16 +340,18 @@ def generate(self, args, thread_number): for i, dir_entry in enumerate(self.dir_list): instructions.push_args(i) instructions.append('DIRECTORY_CHANGE') - if dir_entry.is_directory: + if dir_entry.state.is_directory: instructions.push_args(self.directory_log.key()) instructions.append('DIRECTORY_LOG_DIRECTORY') - if dir_entry.has_known_prefix and dir_entry.is_subspace: - # print '%d. Logging subspace: %d' % (i, dir_entry.dir_id) + if dir_entry.state.has_known_prefix and dir_entry.state.is_subspace: + # print('%d. Logging subspace: %d' % (i, dir_entry.dir_id)) instructions.push_args(self.subspace_log.key()) instructions.append('DIRECTORY_LOG_SUBSPACE') if (i + 1) % 100 == 0: test_util.blocking_commit(instructions) + test_util.blocking_commit(instructions) + instructions.push_args(self.stack_subspace.key()) instructions.append('LOG_STACK') @@ -363,11 +399,15 @@ def generate_path(min_length=0): return path -def generate_prefix(allow_empty=True, is_partition=False): - if allow_empty and random.random() < 0.8: +def generate_prefix(require_unique=False, is_partition=False, min_length=1): + fixed_prefix = 'abcdefg' + if not require_unique and min_length == 0 and random.random() < 0.8: return None - elif is_partition or random.random() < 0.5: - length = random.randint(0 if allow_empty else 1, 5) + elif require_unique or is_partition or min_length > len(fixed_prefix) or random.random() < 0.5: + if require_unique: + min_length = max(min_length, 16) + + length = random.randint(min_length, min_length+5) if length == 0: return '' @@ -377,6 +417,6 @@ def generate_prefix(allow_empty=True, is_partition=False): else: return ''.join(chr(random.randrange(ord('\x02'), ord('\x14'))) for i in range(0, length)) else: - prefix = 'abcdefg' - generated = prefix[0:random.randrange(0 if allow_empty else 1, len(prefix))] + prefix = fixed_prefix + generated = prefix[0:random.randrange(min_length, len(prefix))] return generated diff --git a/bindings/bindingtester/tests/directory_state_tree.py b/bindings/bindingtester/tests/directory_state_tree.py new file mode 100644 index 00000000000..1191049c034 --- /dev/null +++ b/bindings/bindingtester/tests/directory_state_tree.py @@ -0,0 +1,259 @@ +import sys + +class TreeNodeState: + def __init__(self, node, dir_id, is_directory, is_subspace, has_known_prefix, root, is_partition): + self.dir_id = dir_id + self.is_directory = is_directory + self.is_subspace = is_subspace + self.has_known_prefix = has_known_prefix + self.root = root + self.is_partition = is_partition + + self.parents = { node } + self.children = {} + self.deleted = False + +# Represents an element of the directory hierarchy. As a result of various operations (e.g. moves) that +# may or may not have succeeded, a node can represent multiple possible states. +class DirectoryStateTreeNode: + # A cache of directory layers. We mustn't have multiple entries for the same layer + layers = {} + + # Because our operations may be applied to the default directory in the case that + # the current directory failed to open/create, we compute the result of each operation + # as if it was performed on the current directory and the default directory. + default_directory = None + + # Used for debugging + dir_id = 0 + + @classmethod + def reset(cls): + cls.dir_id = 0 + cls.layers = {} + cls.default_directory = None + + @classmethod + def set_default_directory(cls, default_directory): + cls.default_directory = default_directory + + @classmethod + def get_layer(cls, node_subspace_prefix): + if node_subspace_prefix not in DirectoryStateTreeNode.layers: + DirectoryStateTreeNode.layers[node_subspace_prefix] = DirectoryStateTreeNode(True, False, has_known_prefix=False) + + return DirectoryStateTreeNode.layers[node_subspace_prefix] + + def __init__(self, is_directory, is_subspace, has_known_prefix=True, root=None, is_partition=False): + self.state = TreeNodeState(self, DirectoryStateTreeNode.dir_id + 1, is_directory, is_subspace, has_known_prefix, + root or self, is_partition) + DirectoryStateTreeNode.dir_id += 1 + + def __repr__(self): + return '{DirEntry %d: %d}' % (self.state.dir_id, self.state.has_known_prefix) + + def _get_descendent(self, subpath, default): + if not subpath: + if default is not None: + self._merge(default) + return self + + default_child = None + if default is not None: + default_child = default.state.children.get(subpath[0]) + + self_child = self.state.children.get(subpath[0]) + + if self_child is None: + if default_child is None: + return None + else: + return default_child._get_descendent(subpath[1:], None) + + return self_child._get_descendent(subpath[1:], default_child) + + def get_descendent(self, subpath): + return self._get_descendent(subpath, DirectoryStateTreeNode.default_directory) + + def add_child(self, subpath, child): + child.state.root = self.state.root + if DirectoryStateTreeNode.default_directory: + # print('Adding child %r to default directory at %r' % (child, subpath)) + child = DirectoryStateTreeNode.default_directory._add_child_impl(subpath, child) + # print('Added %r' % child) + + # print('Adding child %r to directory at %r' % (child, subpath)) + c = self._add_child_impl(subpath, child) + + # print('Added %r' % c) + return c + + def _add_child_impl(self, subpath, child): + # print('%d, %d. Adding child %r (recursive): %r' % (self.state.dir_id, child.state.dir_id, child, subpath)) + if len(subpath) == 0: + # print('%d, %d. Setting child: %d, %d' % (self.state.dir_id, child.state.dir_id, self.state.has_known_prefix, child.state.has_known_prefix)) + self._merge(child) + return self + else: + if not subpath[0] in self.state.children: + # print('%d, %d. Path %r was absent from %r (%r)' % (self.state.dir_id, child.state.dir_id, subpath[0:1], self, self.state.children)) + subdir = DirectoryStateTreeNode(True, True, root=self.state.root) + self.state.children[subpath[0]] = subdir + else: + subdir = self.state.children[subpath[0]] + # print('%d, %d. Path was present' % (self.state.dir_id, child.state.dir_id)) + + if len(subpath) > 1: + subdir.state.has_known_prefix = False + + return subdir._add_child_impl(subpath[1:], child) + + def _merge(self, other): + if self.state.dir_id == other.state.dir_id: + return + + self.dir_id = other.dir_id + self.state.dir_id = min(other.state.dir_id, self.state.dir_id) + self.state.is_directory = self.state.is_directory and other.state.is_directory + self.state.is_subspace = self.state.is_subspace and other.state.is_subspace + self.state.has_known_prefix = self.state.has_known_prefix and other.state.has_known_prefix + self.state.deleted = self.state.deleted or other.state.deleted + self.state.is_partition = self.state.is_partition or other.state.is_partition + + other_children = other.state.children.copy() + other_parents = other.state.parents.copy() + + for node in other_parents: + node.state = self.state + self.state.parents.add(node) + + for c in other_children: + if c not in self.state.children: + self.state.children[c] = other_children[c] + else: + self.state.children[c]._merge(other_children[c]) + + def _delete_impl(self): + if not self.state.deleted: + self.state.deleted = True + for c in self.state.children.values(): + c._delete_impl() + + def delete(self, path): + child = self.get_descendent(path) + if child: + child._delete_impl() + +def validate_dir(dir, root): + if dir.state.is_directory: + assert dir.state.root == root + else: + assert dir.state.root == dir + +def run_test(): + all_entries = [] + + root = DirectoryStateTreeNode.get_layer('\xfe') + all_entries.append(root) + + default_dir = root.add_child(('default',), DirectoryStateTreeNode(True, True, has_known_prefix=True)) + DirectoryStateTreeNode.set_default_directory(default_dir) + all_entries.append(default_dir) + + all_entries.append(default_dir.add_child(('1',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(default_dir.add_child(('1', '1'), DirectoryStateTreeNode(True, False, has_known_prefix=True))) + all_entries.append(default_dir.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(default_dir.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=False))) + all_entries.append(default_dir.add_child(('5',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(default_dir.add_child(('3', '1'), DirectoryStateTreeNode(True, True, has_known_prefix=False))) + all_entries.append(default_dir.add_child(('1', '3'), DirectoryStateTreeNode(True, True, has_known_prefix=False))) + + entry = all_entries[-1] + child_entries = [] + child_entries.append(entry.add_child(('1',), DirectoryStateTreeNode(True, False, has_known_prefix=True))) + child_entries.append(entry.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + child_entries.append(entry.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + child_entries.append(entry.add_child(('4',), DirectoryStateTreeNode(True, False, has_known_prefix=False))) + child_entries.append(entry.add_child(('5',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + + all_entries.append(root.add_child(('1', '2'), DirectoryStateTreeNode(True, True, has_known_prefix=False))) + all_entries.append(root.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(root.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(root.add_child(('1', '3',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + + # This directory was merged with the default, but both have readable prefixes + entry = root.get_descendent(('2',)) + assert entry.state.has_known_prefix + + entry = all_entries[-1] + all_entries.append(entry.add_child(('1',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + all_entries.append(entry.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=False))) + all_entries.append(entry.add_child(('3',), DirectoryStateTreeNode(True, False, has_known_prefix=True))) + + entry_to_move = all_entries[-1] + + all_entries.append(entry.add_child(('5',), DirectoryStateTreeNode(True, False, has_known_prefix=True))) + child_entries.append(entry.add_child(('6',), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + + all_entries.extend(child_entries) + + # This directory has an unknown prefix + entry = root.get_descendent(('1', '2')) + assert not entry.state.has_known_prefix + + # This directory was default created and should have an unknown prefix + # It will merge with the default directory's child, which is not a subspace + entry = root.get_descendent(('1',)) + assert not entry.state.has_known_prefix + assert not entry.state.is_subspace + + # Multiple merges will have made this prefix unreadable + entry = root.get_descendent(('2',)) + assert not entry.state.has_known_prefix + + # Merge with default directory's child that has an unknown prefix + entry = root.get_descendent(('3',)) + assert not entry.state.has_known_prefix + + # Merge with default directory's child that has an unknown prefix and merged children + entry = root.get_descendent(('1', '3')) + assert set(entry.state.children.keys()) == {'1', '2', '3', '4', '5', '6'} + + # This child entry should be the combination of ['default', '3'], ['default', '1', '3'], and ['1', '3'] + entry = entry.get_descendent(('3',)) + assert not entry.state.has_known_prefix + assert not entry.state.is_subspace + + # Verify the merge of the children + assert not child_entries[0].state.has_known_prefix + assert not child_entries[0].state.is_subspace + + assert not child_entries[1].state.has_known_prefix + assert child_entries[1].state.is_subspace + + assert not child_entries[2].state.has_known_prefix + assert not child_entries[2].state.is_subspace + + assert not child_entries[3].state.has_known_prefix + assert not child_entries[3].state.is_subspace + + assert child_entries[4].state.has_known_prefix + assert not child_entries[4].state.is_subspace + + assert child_entries[5].state.has_known_prefix + assert child_entries[5].state.is_subspace + + entry = root.add_child(('3',), entry_to_move) + all_entries.append(entry) + + # Test moving an entry + assert not entry.state.has_known_prefix + assert not entry.state.is_subspace + assert entry.state.children.keys() == ['1'] + + for e in all_entries: + validate_dir(e, root) + +if __name__ == '__main__': + sys.exit(run_test()) + diff --git a/bindings/bindingtester/tests/directory_util.py b/bindings/bindingtester/tests/directory_util.py index 3a95ed848e2..041d5369d9a 100644 --- a/bindings/bindingtester/tests/directory_util.py +++ b/bindings/bindingtester/tests/directory_util.py @@ -27,6 +27,7 @@ from bindingtester import util from bindingtester.tests import test_util +from bindingtester.tests.directory_state_tree import DirectoryStateTreeNode fdb.api_version(FDB_API_VERSION) @@ -34,82 +35,26 @@ DEFAULT_DIRECTORY_PREFIX = 'default' DIRECTORY_ERROR_STRING = 'DIRECTORY_ERROR' - -class DirListEntry: - dir_id = 0 # Used for debugging - - def __init__(self, is_directory, is_subspace, has_known_prefix=True, path=(), root=None): - self.root = root or self - self.path = path - self.is_directory = is_directory - self.is_subspace = is_subspace - self.has_known_prefix = has_known_prefix - self.children = {} - - self.dir_id = DirListEntry.dir_id + 1 - DirListEntry.dir_id += 1 - - def __repr__(self): - return 'DirEntry %d %r: %d' % (self.dir_id, self.path, self.has_known_prefix) - - def add_child(self, subpath, default_path, root, child): - if default_path in root.children: - # print 'Adding child %r to default directory %r at %r' % (child, root.children[DirectoryTest.DEFAULT_DIRECTORY_PATH].path, subpath) - c = root.children[default_path]._add_child_impl(subpath, child) - child.has_known_prefix = c.has_known_prefix and child.has_known_prefix - # print 'Added %r' % c - - # print 'Adding child %r to directory %r at %r' % (child, self.path, subpath) - c = self._add_child_impl(subpath, child) - # print 'Added %r' % c - return c - - def _add_child_impl(self, subpath, child): - # print '%d, %d. Adding child (recursive): %s %s' % (self.dir_id, child.dir_id, repr(self.path), repr(subpath)) - if len(subpath) == 0: - self.has_known_prefix = self.has_known_prefix and child.has_known_prefix - # print '%d, %d. Setting child: %d' % (self.dir_id, child.dir_id, self.has_known_prefix) - self._merge_children(child) - - return self - else: - if not subpath[0] in self.children: - # print '%d, %d. Path %s was absent (%s)' % (self.dir_id, child.dir_id, repr(self.path + subpath[0:1]), repr(self.children)) - subdir = DirListEntry(True, True, path=self.path + subpath[0:1], root=self.root) - subdir.has_known_prefix = len(subpath) == 1 - self.children[subpath[0]] = subdir - else: - subdir = self.children[subpath[0]] - subdir.has_known_prefix = False - # print '%d, %d. Path was present' % (self.dir_id, child.dir_id) - - return subdir._add_child_impl(subpath[1:], child) - - def _merge_children(self, other): - for c in other.children: - if c not in self.children: - self.children[c] = other.children[c] - else: - self.children[c].has_known_prefix = self.children[c].has_known_prefix and other.children[c].has_known_prefix - self.children[c]._merge_children(other.children[c]) - - def setup_directories(instructions, default_path, random): - dir_list = [DirListEntry(True, False, True)] + # Clients start with the default directory layer in the directory list + DirectoryStateTreeNode.reset() + dir_list = [DirectoryStateTreeNode.get_layer('\xfe')] + instructions.push_args(0, '\xfe') instructions.append('DIRECTORY_CREATE_SUBSPACE') - dir_list.append(DirListEntry(False, True)) + dir_list.append(DirectoryStateTreeNode(False, True)) instructions.push_args(0, '') instructions.append('DIRECTORY_CREATE_SUBSPACE') - dir_list.append(DirListEntry(False, True)) + dir_list.append(DirectoryStateTreeNode(False, True)) instructions.push_args(1, 2, 1) instructions.append('DIRECTORY_CREATE_LAYER') - dir_list.append(DirListEntry(True, False, True)) + dir_list.append(DirectoryStateTreeNode.get_layer('\xfe')) create_default_directory_subspace(instructions, default_path, random) - dir_list.append(DirListEntry(True, True, True)) + dir_list.append(dir_list[0].add_child((default_path,), DirectoryStateTreeNode(True, True, has_known_prefix=True))) + DirectoryStateTreeNode.set_default_directory(dir_list[-1]) instructions.push_args(DEFAULT_DIRECTORY_INDEX) instructions.append('DIRECTORY_SET_ERROR_INDEX') diff --git a/bindings/bindingtester/tests/scripted.py b/bindings/bindingtester/tests/scripted.py index dd768957b9f..a04c21f1805 100644 --- a/bindings/bindingtester/tests/scripted.py +++ b/bindings/bindingtester/tests/scripted.py @@ -54,6 +54,8 @@ def setup(self, args): if args.bisect: raise Exception('Scripted tests cannot be bisected') + self.api_version = args.api_version + def generate(self, args, thread_number): self.results = [] @@ -272,12 +274,17 @@ def generate(self, args, thread_number): stampKey = 'stampedXXXXXXXXXXsuffix' stampKeyIndex = stampKey.find('XXXXXXXXXX') - stampKeyStr = chr(stampKeyIndex % 256) + chr(stampKeyIndex / 256) - main_thread.push_args(u'SET_VERSIONSTAMPED_KEY', stampKey + stampKeyStr, 'stampedBar') + main_thread.push_args(u'SET_VERSIONSTAMPED_KEY', self.versionstamp_key(stampKey, stampKeyIndex), 'stampedBar') main_thread.append('ATOMIC_OP') - main_thread.push_args(u'SET_VERSIONSTAMPED_VALUE', 'stampedValue', 'XXXXXXXXXX') + main_thread.push_args(u'SET_VERSIONSTAMPED_VALUE', 'stampedValue', self.versionstamp_value('XXXXXXXXXX')) main_thread.append('ATOMIC_OP') + if self.api_version >= 520: + stampValue = 'stampedXXXXXXXXXXsuffix' + stampValueIndex = stampValue.find('XXXXXXXXXX') + main_thread.push_args(u'SET_VERSIONSTAMPED_VALUE', 'stampedValue2', self.versionstamp_value(stampValue, stampValueIndex)) + main_thread.append('ATOMIC_OP') + main_thread.push_args('suffix') main_thread.append('GET_VERSIONSTAMP') test_util.blocking_commit(main_thread) @@ -296,6 +303,12 @@ def generate(self, args, thread_number): main_thread.append('GET') self.add_result(main_thread, args, 'stampedBar') + if self.api_version >= 520: + main_thread.push_args('stampedValue2') + main_thread.append('GET') + main_thread.append('GET') + self.add_result(main_thread, args, 'stampedBar') + main_thread.append('GET_VERSIONSTAMP') test_util.blocking_commit(main_thread) self.add_result(main_thread, args, 'RESULT_NOT_PRESENT') diff --git a/bindings/c/fdb_c.cpp b/bindings/c/fdb_c.cpp index a51d51a7e2f..2f2dd0ee83e 100644 --- a/bindings/c/fdb_c.cpp +++ b/bindings/c/fdb_c.cpp @@ -42,7 +42,10 @@ int g_api_version = 0; #define CLUSTER(c) ((ICluster*)c) #define TXN(t) ((ITransaction*)t) -#define API (MultiVersionApi::api) +/* + * While we could just use the MultiVersionApi instance directly, this #define allows us to swap in any other IClientApi instance (e.g. from ThreadSafeApi) + */ +#define API ((IClientApi*)MultiVersionApi::api) /* This must be true so that we can return the data pointer of a Standalone as an array of FDBKeyValue. */ diff --git a/bindings/c/foundationdb/fdb_c.h b/bindings/c/foundationdb/fdb_c.h index 059c88e0a49..1c96c5914f0 100644 --- a/bindings/c/foundationdb/fdb_c.h +++ b/bindings/c/foundationdb/fdb_c.h @@ -241,7 +241,7 @@ extern "C" { fdb_transaction_get_committed_version( FDBTransaction* tr, int64_t* out_version ); - DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_get_versionstamp( FDBTransaction* tr ); + DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_get_versionstamp( FDBTransaction* tr ); DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_on_error( FDBTransaction* tr, fdb_error_t error ); @@ -276,7 +276,7 @@ extern "C" { DLLEXPORT fdb_bool_t fdb_future_is_error( FDBFuture* f ); #else - #define fdb_future_is_error(x) FDB_REMOVED_FUNCTION + #define fdb_future_is_error(x) FDB_REMOVED_FUNCTION #endif #if FDB_API_VERSION < 14 @@ -307,7 +307,7 @@ extern "C" { int end_key_name_length, fdb_bool_t end_or_equal, int end_offset, int limit ); #else - #define fdb_transaction_get_range_selector(tr,bkn,bknl,boe,bo,ekn,eknl,eoe,eo,lim) FDB_REMOVED_FUNCTION + #define fdb_transaction_get_range_selector(tr,bkn,bknl,boe,bo,ekn,eknl,eoe,eo,lim) FDB_REMOVED_FUNCTION #endif #ifdef __cplusplus diff --git a/bindings/c/test/performance_test.c b/bindings/c/test/performance_test.c index 953953c1f6d..50aa189da8d 100644 --- a/bindings/c/test/performance_test.c +++ b/bindings/c/test/performance_test.c @@ -44,7 +44,6 @@ fdb_error_t waitError(FDBFuture *f) { struct RunResult run(struct ResultSet *rs, FDBDatabase *db, struct RunResult (*func)(struct ResultSet*, FDBTransaction*)) { FDBTransaction *tr = NULL; - checkError(fdb_database_create_transaction(db, &tr), "create transaction", rs); fdb_error_t e = fdb_database_create_transaction(db, &tr); checkError(e, "create transaction", rs); @@ -62,9 +61,11 @@ struct RunResult run(struct ResultSet *rs, FDBDatabase *db, struct RunResult (*f fdb_error_t retryE = waitError(f); fdb_future_destroy(f); if (retryE) { + fdb_transaction_destroy(tr); return (struct RunResult) {0, retryE}; } } else { + fdb_transaction_destroy(tr); return r; } } diff --git a/bindings/c/test/ryw_benchmark.c b/bindings/c/test/ryw_benchmark.c index e7c291209dd..06a2dc01b72 100644 --- a/bindings/c/test/ryw_benchmark.c +++ b/bindings/c/test/ryw_benchmark.c @@ -236,6 +236,7 @@ void runTests(struct ResultSet *rs) { runTest(&clearRangeGetRange, tr, rs, "C: get range cached values with clear ranges throughput"); runTest(&interleavedSetsGets, tr, rs, "C: interleaved sets and gets on a single key throughput"); + fdb_transaction_destroy(tr); fdb_database_destroy(db); fdb_stop_network(); } diff --git a/bindings/flow/fdb_flow.actor.cpp b/bindings/flow/fdb_flow.actor.cpp index fa3f41b439c..88045ba10e3 100644 --- a/bindings/flow/fdb_flow.actor.cpp +++ b/bindings/flow/fdb_flow.actor.cpp @@ -33,7 +33,7 @@ THREAD_FUNC networkThread(void* fdb) { } ACTOR Future _test() { - API *fdb = FDB::API::selectAPIVersion(510); + API *fdb = FDB::API::selectAPIVersion(520); auto c = fdb->createCluster( std::string() ); auto db = c->createDatabase(); state Reference tr( new Transaction(db) ); @@ -77,7 +77,7 @@ ACTOR Future _test() { } void fdb_flow_test() { - API *fdb = FDB::API::selectAPIVersion(510); + API *fdb = FDB::API::selectAPIVersion(520); fdb->setupNetwork(); startThread(networkThread, fdb); diff --git a/bindings/flow/tester/Tester.actor.cpp b/bindings/flow/tester/Tester.actor.cpp index 038580ec910..a572f70b040 100644 --- a/bindings/flow/tester/Tester.actor.cpp +++ b/bindings/flow/tester/Tester.actor.cpp @@ -1538,7 +1538,6 @@ struct UnitTestsFunc : InstructionFunc { tr->setOption(FDBTransactionOption::FDB_TR_OPTION_READ_YOUR_WRITES_DISABLE); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_READ_SYSTEM_KEYS); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_ACCESS_SYSTEM_KEYS); - tr->setOption(FDBTransactionOption::FDB_TR_OPTION_DURABILITY_DEV_NULL_IS_WEB_SCALE); const uint64_t timeout = 60*1000; tr->setOption(FDBTransactionOption::FDB_TR_OPTION_TIMEOUT, Optional(StringRef((const uint8_t*)&timeout, 8))); const uint64_t retryLimit = 50; @@ -1619,11 +1618,7 @@ ACTOR static Future doInstructions(Reference data) { } // Flow directory operations don't support snapshot reads - if (isDirectory && isSnapshot) { - Version readVersion = wait(instruction->tr->getReadVersion()); - instruction->tr = Reference(new Transaction(data->db)); - instruction->tr->setVersion(readVersion); - } + ASSERT(!isDirectory || !isSnapshot); data->stack.index = idx; Void _ = wait(InstructionFunc::call(op.toString(), data, instruction)); @@ -1744,7 +1739,7 @@ ACTOR void _test_versionstamp() { try { g_network = newNet2(NetworkAddress(), false); - API *fdb = FDB::API::selectAPIVersion(510); + API *fdb = FDB::API::selectAPIVersion(520); fdb->setupNetwork(); startThread(networkThread, fdb); @@ -1755,7 +1750,7 @@ ACTOR void _test_versionstamp() { state Future> ftrVersion = tr->getVersionstamp(); - tr->atomicOp(LiteralStringRef("foo"), LiteralStringRef("blahblahbl"), FDBMutationType::FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_VALUE); + tr->atomicOp(LiteralStringRef("foo"), LiteralStringRef("blahblahbl\x00\x00\x00\x00"), FDBMutationType::FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_VALUE); Void _ = wait(tr->commit()); // should use retry loop diff --git a/bindings/go/README.md b/bindings/go/README.md index 476f93d7525..c262d5eca0c 100644 --- a/bindings/go/README.md +++ b/bindings/go/README.md @@ -1,12 +1,12 @@ fdb-go ====== -[Go language](http://golang.org) bindings for [FoundationDB](https://www.foundationdb.org/documentation/), a distributed key-value store with ACID transactions. +[Go language](http://golang.org) bindings for [FoundationDB](https://apple.github.io/foundationdb/index.html#documentation), a distributed key-value store with ACID transactions. This package requires: - Go 1.1+ with CGO enabled -- FoundationDB C API 2.0.x, 3.0.x, or 4.x.y (part of the [FoundationDB clients package](https://www.foundationdb.org/downloads/fdb-c/)) +- FoundationDB C API 2.0.x, 3.0.x, or 4.x.y (part of the [FoundationDB clients package](https://apple.github.io/foundationdb/downloads.html#c)) Use of this package requires the selection of a FoundationDB API version at runtime. This package currently supports FoundationDB API versions 200-520. @@ -28,4 +28,4 @@ Documentation ------------- * [API documentation](https://godoc.org/github.com/apple/foundationdb/bindings/go/src/fdb) -* [Tutorial](https://www.foundationdb.org/documentation/class-scheduling-go.html) +* [Tutorial](https://apple.github.io/foundationdb/class-scheduling.html) diff --git a/bindings/go/fdb-go-install.sh b/bindings/go/fdb-go-install.sh index a18da32480f..f629fada663 100755 --- a/bindings/go/fdb-go-install.sh +++ b/bindings/go/fdb-go-install.sh @@ -167,7 +167,7 @@ else if [[ "${status}" -eq 0 ]] ; then destdir=$( cd "${destdir}" && pwd ) # Get absolute path of destination dir. - fdbdir="${destdir}/foundation" + fdbdir="${destdir}/foundationdb" if [[ ! -d "${destdir}" ]] ; then cmd=("mkdir" "-p" "${destdir}") diff --git a/bindings/go/src/_stacktester/stacktester.go b/bindings/go/src/_stacktester/stacktester.go index 6140bfc4361..c5cbd8852f6 100644 --- a/bindings/go/src/_stacktester/stacktester.go +++ b/bindings/go/src/_stacktester/stacktester.go @@ -787,7 +787,6 @@ func (sm *StackMachine) processInst(idx int, inst tuple.Tuple) { tr.Options().SetReadYourWritesDisable() tr.Options().SetReadSystemKeys() tr.Options().SetAccessSystemKeys() - tr.Options().SetDurabilityDevNullIsWebScale() tr.Options().SetTimeout(60 * 1000) tr.Options().SetRetryLimit(50) tr.Options().SetMaxRetryDelay(100) diff --git a/bindings/go/src/fdb/database.go b/bindings/go/src/fdb/database.go index 7b75e6da1fa..6a8a678db8f 100644 --- a/bindings/go/src/fdb/database.go +++ b/bindings/go/src/fdb/database.go @@ -111,7 +111,7 @@ func retryable(wrapped func() (interface{}, error), onError func(Error) FutureNi // retried or, if fatal, return the error to the caller. // // When working with Future objects in a transactional function, you may either -// explicity check and return error values using Get, or call MustGet. Transact +// explicitly check and return error values using Get, or call MustGet. Transact // will recover a panicked Error and either retry the transaction or return the // error. // @@ -151,7 +151,7 @@ func (d Database) Transact(f func(Transaction) (interface{}, error)) (interface{ // retried or, if fatal, return the error to the caller. // // When working with Future objects in a read-only transactional function, you -// may either explicity check and return error values using Get, or call +// may either explicitly check and return error values using Get, or call // MustGet. ReadTransact will recover a panicked Error and either retry the // transaction or return the error. // diff --git a/bindings/go/src/fdb/directory/directory.go b/bindings/go/src/fdb/directory/directory.go index 21f576d1fd3..792621b3860 100644 --- a/bindings/go/src/fdb/directory/directory.go +++ b/bindings/go/src/fdb/directory/directory.go @@ -26,7 +26,7 @@ // // For general guidance on directory usage, see the Directories section of the // Developer Guide -// (https://www.foundationdb.org/documentation/developer-guide.html#developer-guide-directories). +// (https://apple.github.io/foundationdb/developer-guide.html#directories). // // Directories are identified by hierarchical paths analogous to the paths in a // Unix-like file system. A path is represented as a slice of strings. Each diff --git a/bindings/go/src/fdb/directory/directoryLayer.go b/bindings/go/src/fdb/directory/directoryLayer.go index 14064b53a70..85e6b2a93d5 100644 --- a/bindings/go/src/fdb/directory/directoryLayer.go +++ b/bindings/go/src/fdb/directory/directoryLayer.go @@ -27,6 +27,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/apple/foundationdb/bindings/go/src/fdb" "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" @@ -78,9 +79,8 @@ func (dl directoryLayer) createOrOpen(rtr fdb.ReadTransaction, tr *fdb.Transacti if prefix != nil && !dl.allowManualPrefixes { if len(dl.path) == 0 { return nil, errors.New("cannot specify a prefix unless manual prefixes are enabled") - } else { - return nil, errors.New("cannot specify a prefix in a partition") } + return nil, errors.New("cannot specify a prefix in a partition") } if len(path) == 0 { @@ -562,9 +562,8 @@ func (dl directoryLayer) contentsOfNode(node subspace.Subspace, path []string, l ndl := NewDirectoryLayer(subspace.FromBytes(nssb), ss, false).(directoryLayer) ndl.path = newPath return directoryPartition{ndl, dl}, nil - } else { - return directorySubspace{ss, dl, newPath, layer}, nil } + return directorySubspace{ss, dl, newPath, layer}, nil } func (dl directoryLayer) nodeWithPrefix(prefix []byte) subspace.Subspace { diff --git a/bindings/go/src/fdb/directory/directoryPartition.go b/bindings/go/src/fdb/directory/directoryPartition.go index 0a16870e7bb..d6e0275f02a 100644 --- a/bindings/go/src/fdb/directory/directoryPartition.go +++ b/bindings/go/src/fdb/directory/directoryPartition.go @@ -72,9 +72,8 @@ func (dp directoryPartition) GetLayer() []byte { func (dp directoryPartition) getLayerForPath(path []string) directoryLayer { if len(path) == 0 { return dp.parentDirectoryLayer - } else { - return dp.directoryLayer } + return dp.directoryLayer } func (dp directoryPartition) MoveTo(t fdb.Transactor, newAbsolutePath []string) (DirectorySubspace, error) { diff --git a/bindings/go/src/fdb/doc.go b/bindings/go/src/fdb/doc.go index fba7f6432bb..40720f7afd6 100644 --- a/bindings/go/src/fdb/doc.go +++ b/bindings/go/src/fdb/doc.go @@ -25,12 +25,12 @@ Package fdb provides an interface to FoundationDB databases (version 2.0 or high To build and run programs using this package, you must have an installed copy of the FoundationDB client libraries (version 2.0.0 or later), available for Linux, -Windows and OS X at https://www.foundationdb.org/downloads/fdb-c/. +Windows and OS X at https://www.foundationdb.org/download/. This documentation specifically applies to the FoundationDB Go binding. For more extensive guidance to programming with FoundationDB, as well as API documentation for the other FoundationDB interfaces, please see -https://www.foundationdb.org/documentation/index.html. +https://apple.github.io/foundationdb/index.html. Basic Usage @@ -46,7 +46,7 @@ A basic interaction with the FoundationDB API is demonstrated below: func main() { // Different API versions may expose different runtime behaviors. - fdb.MustAPIVersion(200) + fdb.MustAPIVersion(520) // Open the default database from the system cluster db := fdb.MustOpenDefault() @@ -198,7 +198,7 @@ operations perform different transformations. Like other database operations, an atomic operation is used within a transaction. For more information on atomic operations in FoundationDB, please see -https://www.foundationdb.org/documentation/developer-guide.html#atomic-operations. The +https://apple.github.io/foundationdb/developer-guide.html#atomic-operations. The operands to atomic operations in this API must be provided as appropriately encoded byte slices. To convert a Go type to a byte slice, see the binary package. diff --git a/bindings/go/src/fdb/errors.go b/bindings/go/src/fdb/errors.go index 676e5dabf70..0f94195a58b 100644 --- a/bindings/go/src/fdb/errors.go +++ b/bindings/go/src/fdb/errors.go @@ -37,7 +37,7 @@ import ( // as a panic from any FoundationDB API function whose name ends with OrPanic. // // You may compare the Code field of an Error against the list of FoundationDB -// error codes at https://www.foundationdb.org/documentation/api-error-codes.html, +// error codes at https://apple.github.io/foundationdb/api-error-codes.html, // but generally an Error should be passed to (Transaction).OnError. When using // (Database).Transact, non-fatal errors will be retried automatically. type Error struct { diff --git a/bindings/go/src/fdb/fdb.go b/bindings/go/src/fdb/fdb.go index 6968af5d5ac..33a7a5b62c9 100644 --- a/bindings/go/src/fdb/fdb.go +++ b/bindings/go/src/fdb/fdb.go @@ -139,9 +139,8 @@ func APIVersion(version int) error { maxSupportedVersion := C.fdb_get_max_api_version() if headerVersion > int(maxSupportedVersion) { return fmt.Errorf("This version of the FoundationDB Go binding is not supported by the installed FoundationDB C library. The binding requires a library that supports API version %d, but the installed library supports a maximum version of %d.", version, maxSupportedVersion) - } else { - return fmt.Errorf("API version %d is not supported by the installed FoundationDB C library.", version) } + return fmt.Errorf("API version %d is not supported by the installed FoundationDB C library.", version) } return Error{int(e)} } @@ -194,12 +193,17 @@ var apiVersion int var networkStarted bool var networkMutex sync.Mutex +type DatabaseId struct { + clusterFile string + dbName string +} + var openClusters map[string]Cluster -var openDatabases map[string]Database +var openDatabases map[DatabaseId]Database func init() { openClusters = make(map[string]Cluster) - openDatabases = make(map[string]Database) + openDatabases = make(map[DatabaseId]Database) } func startNetwork() error { @@ -289,13 +293,13 @@ func Open(clusterFile string, dbName []byte) (Database, error) { openClusters[clusterFile] = cluster } - db, ok := openDatabases[string(dbName)] + db, ok := openDatabases[DatabaseId{clusterFile, string(dbName)}] if !ok { db, e = cluster.OpenDatabase(dbName) if e != nil { return Database{}, e } - openDatabases[string(dbName)] = db + openDatabases[DatabaseId{clusterFile, string(dbName)}] = db } return db, nil @@ -355,9 +359,8 @@ func CreateCluster(clusterFile string) (Cluster, error) { func byteSliceToPtr(b []byte) *C.uint8_t { if len(b) > 0 { return (*C.uint8_t)(unsafe.Pointer(&b[0])) - } else { - return nil } + return nil } // A KeyConvertible can be converted to a FoundationDB Key. All functions in the diff --git a/bindings/go/src/fdb/generated.go b/bindings/go/src/fdb/generated.go index f5fff5c0e6f..9f71b149f72 100644 --- a/bindings/go/src/fdb/generated.go +++ b/bindings/go/src/fdb/generated.go @@ -159,6 +159,27 @@ func (o NetworkOptions) SetBuggifySectionFiredProbability(param int64) error { return o.setOpt(51, b) } +// Set the ca bundle +// +// Parameter: ca bundle +func (o NetworkOptions) SetTLSCaBytes(param []byte) error { + return o.setOpt(52, param) +} + +// Set the file from which to load the certificate authority bundle +// +// Parameter: file path +func (o NetworkOptions) SetTLSCaPath(param string) error { + return o.setOpt(53, []byte(param)) +} + +// Set the passphrase for encrypted private key. Password should be set before setting the key for the password to be used. +// +// Parameter: key passphrase +func (o NetworkOptions) SetTLSPassword(param string) error { + return o.setOpt(54, []byte(param)) +} + // Disables the multi-version client API and instead uses the local client directly. Must be set before setting up the network. func (o NetworkOptions) SetDisableMultiVersionClientApi() error { return o.setOpt(60, nil) @@ -269,11 +290,6 @@ func (o TransactionOptions) SetDurabilityRisky() error { return o.setOpt(120, nil) } -// Not yet implemented. -func (o TransactionOptions) SetDurabilityDevNullIsWebScale() error { - return o.setOpt(130, nil) -} - // Specifies that this transaction should be treated as highest priority and that lower priority transactions should block behind this one. Use is discouraged outside of low-level tools func (o TransactionOptions) SetPrioritySystemImmediate() error { return o.setOpt(200, nil) @@ -450,12 +466,12 @@ func (t Transaction) Min(key KeyConvertible, param []byte) { t.atomicOp(key.FDBKey(), param, 13) } -// SetVersionstampedKey transforms ``key`` using a versionstamp for the transaction. Sets the transformed key in the database to ``param``. A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database. The last 2 bytes are monotonic in the serialization order for transactions. WARNING: At this time versionstamps are compatible with the Tuple layer only in the Java and Python bindings. Note that this implies versionstamped keys may not be used with the Subspace and Directory layers except in those languages. +// SetVersionstampedKey transforms ``key`` using a versionstamp for the transaction. Sets the transformed key in the database to ``param``. The key is transformed by removing the final four bytes from the key and reading those as a little-Endian 32-bit integer to get a position ``pos``. The 10 bytes of the key from ``pos`` to ``pos + 10`` are replaced with the versionstamp of the transaction used. The first byte of the key is position 0. A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database (serialized in big-Endian order). The last 2 bytes are monotonic in the serialization order for transactions. WARNING: At this time, versionstamps are compatible with the Tuple layer only in the Java and Python bindings. Also, note that prior to API version 520, the offset was computed from only the final two bytes rather than the final four bytes. func (t Transaction) SetVersionstampedKey(key KeyConvertible, param []byte) { t.atomicOp(key.FDBKey(), param, 14) } -// SetVersionstampedValue transforms ``param`` using a versionstamp for the transaction. Sets ``key`` in the database to the transformed parameter. A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database. The last 2 bytes are monotonic in the serialization order for transactions. WARNING: At this time versionstamped values are not compatible with the Tuple layer. +// SetVersionstampedValue transforms ``param`` using a versionstamp for the transaction. Sets the ``key`` given to the transformed ``param``. The parameter is transformed by removing the final four bytes from ``param`` and reading those as a little-Endian 32-bit integer to get a position ``pos``. The 10 bytes of the parameter from ``pos`` to ``pos + 10`` are replaced with the versionstamp of the transaction used. The first byte of the parameter is position 0. A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database (serialized in big-Endian order). The last 2 bytes are monotonic in the serialization order for transactions. WARNING: At this time, versionstamps are compatible with the Tuple layer only in the Java and Python bindings. Also, note that prior to API version 520, the versionstamp was always placed at the beginning of the parameter rather than computing an offset. func (t Transaction) SetVersionstampedValue(key KeyConvertible, param []byte) { t.atomicOp(key.FDBKey(), param, 15) } diff --git a/bindings/go/src/fdb/keyselector.go b/bindings/go/src/fdb/keyselector.go index 538a626a1cb..7d4f45ffe5c 100644 --- a/bindings/go/src/fdb/keyselector.go +++ b/bindings/go/src/fdb/keyselector.go @@ -34,7 +34,7 @@ type Selectable interface { // // The most common key selectors are constructed with the functions documented // below. For details of how KeySelectors are specified and resolved, see -// https://www.foundationdb.org/documentation/developer-guide.html#key-selectors. +// https://apple.github.io/foundationdb/developer-guide.html#key-selectors. type KeySelector struct { Key KeyConvertible OrEqual bool diff --git a/bindings/go/src/fdb/range.go b/bindings/go/src/fdb/range.go index c0b17662dfa..de951c4518d 100644 --- a/bindings/go/src/fdb/range.go +++ b/bindings/go/src/fdb/range.go @@ -247,7 +247,7 @@ func (ri *RangeIterator) fetchNextBatch() { ri.sr.Begin = FirstGreaterThan(ri.kvs[ri.index-1].Key) } - ri.iteration += 1 + ri.iteration++ f := ri.t.doGetRange(ri.sr, ri.options, ri.snapshot, ri.iteration) ri.f = &f @@ -265,7 +265,7 @@ func (ri *RangeIterator) Get() (kv KeyValue, e error) { kv = ri.kvs[ri.index] - ri.index += 1 + ri.index++ if ri.index == len(ri.kvs) { ri.fetchNextBatch() @@ -291,7 +291,7 @@ func Strinc(prefix []byte) ([]byte, error) { if prefix[i] != 0xFF { ret := make([]byte, i+1) copy(ret, prefix[:i+1]) - ret[i] += 1 + ret[i]++ return ret, nil } } diff --git a/bindings/go/src/fdb/snapshot.go b/bindings/go/src/fdb/snapshot.go index 699344bc659..18c77d79bb3 100644 --- a/bindings/go/src/fdb/snapshot.go +++ b/bindings/go/src/fdb/snapshot.go @@ -28,7 +28,7 @@ package fdb // transaction conflicts but making it harder to reason about concurrency. // // For more information on snapshot reads, see -// https://www.foundationdb.org/documentation/developer-guide.html#snapshot-reads. +// https://apple.github.io/foundationdb/developer-guide.html#snapshot-reads. type Snapshot struct { *transaction } diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index f2523a81ef6..b779d5a9f77 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -29,7 +29,7 @@ // As a best practice, API clients should use at least one subspace for // application data. For general guidance on subspace usage, see the Subspaces // section of the Developer Guide -// (https://www.foundationdb.org/documentation/developer-guide.html#developer-guide-sub-keyspaces). +// (https://apple.github.io/foundationdb/developer-guide.html#subspaces). package subspace import ( diff --git a/bindings/go/src/fdb/transaction.go b/bindings/go/src/fdb/transaction.go index fa4c74334ad..9507536bc15 100644 --- a/bindings/go/src/fdb/transaction.go +++ b/bindings/go/src/fdb/transaction.go @@ -171,7 +171,7 @@ func (t Transaction) SetReadVersion(version int64) { // but making it harder to reason about concurrency. // // For more information on snapshot reads, see -// https://www.foundationdb.org/documentation/developer-guide.html#using-snapshot-reads. +// https://apple.github.io/foundationdb/developer-guide.html#snapshot-reads. func (t Transaction) Snapshot() Snapshot { return Snapshot{t.transaction} } @@ -196,7 +196,7 @@ func (t Transaction) OnError(e Error) FutureNil { // As with other client/server databases, in some failure scenarios a client may // be unable to determine whether a transaction succeeded. For more information, // see -// https://www.foundationdb.org/documentation/developer-guide.html#developer-guide-unknown-results. +// https://apple.github.io/foundationdb/developer-guide.html#transactions-with-unknown-results. func (t Transaction) Commit() FutureNil { return &futureNil{newFuture(C.fdb_transaction_commit(t.ptr))} } @@ -352,9 +352,8 @@ func (t Transaction) Reset() { func boolToInt(b bool) int { if b { return 1 - } else { - return 0 } + return 0 } func (t *transaction) getKey(sel KeySelector, snapshot int) FutureKey { @@ -396,7 +395,7 @@ func addConflictRange(t *transaction, er ExactRange, crtype conflictRangeType) e // conflict. // // For more information on conflict ranges, see -// https://www.foundationdb.org/documentation/developer-guide.html#conflict-ranges. +// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges. func (t Transaction) AddReadConflictRange(er ExactRange) error { return addConflictRange(t.transaction, er, conflictRangeTypeRead) } @@ -413,7 +412,7 @@ func copyAndAppend(orig []byte, b byte) []byte { // this key could cause the transaction to fail with a conflict. // // For more information on conflict ranges, see -// https://www.foundationdb.org/documentation/developer-guide.html#conflict-ranges. +// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges. func (t Transaction) AddReadConflictKey(key KeyConvertible) error { return addConflictRange(t.transaction, KeyRange{key, Key(copyAndAppend(key.FDBKey(), 0x00))}, conflictRangeTypeRead) } @@ -424,7 +423,7 @@ func (t Transaction) AddReadConflictKey(key KeyConvertible) error { // conflict. // // For more information on conflict ranges, see -// https://www.foundationdb.org/documentation/developer-guide.html#conflict-ranges. +// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges. func (t Transaction) AddWriteConflictRange(er ExactRange) error { return addConflictRange(t.transaction, er, conflictRangeTypeWrite) } @@ -434,7 +433,7 @@ func (t Transaction) AddWriteConflictRange(er ExactRange) error { // read this key could fail with a conflict. // // For more information on conflict ranges, see -// https://www.foundationdb.org/documentation/developer-guide.html#conflict-ranges. +// https://apple.github.io/foundationdb/developer-guide.html#conflict-ranges. func (t Transaction) AddWriteConflictKey(key KeyConvertible) error { return addConflictRange(t.transaction, KeyRange{key, Key(copyAndAppend(key.FDBKey(), 0x00))}, conflictRangeTypeWrite) } diff --git a/bindings/go/src/fdb/tuple/testdata/tuples.golden b/bindings/go/src/fdb/tuple/testdata/tuples.golden new file mode 100644 index 00000000000..9f1c3f11dfe Binary files /dev/null and b/bindings/go/src/fdb/tuple/testdata/tuples.golden differ diff --git a/bindings/go/src/fdb/tuple/tuple.go b/bindings/go/src/fdb/tuple/tuple.go index d00a594abf2..aab7d0ed32b 100644 --- a/bindings/go/src/fdb/tuple/tuple.go +++ b/bindings/go/src/fdb/tuple/tuple.go @@ -27,7 +27,7 @@ // of higher-level data models. // // For general guidance on tuple usage, see the Tuple section of Data Modeling -// (https://www.foundationdb.org/documentation/data-modeling.html#data-modeling-tuples). +// (https://apple.github.io/foundationdb/data-modeling.html#tuples). // // FoundationDB tuples can currently encode byte and unicode strings, integers // and NULL values. In Go these are represented as []byte, string, int64 and @@ -38,6 +38,8 @@ import ( "bytes" "encoding/binary" "fmt" + "math" + "github.com/apple/foundationdb/bindings/go/src/fdb" ) @@ -93,6 +95,14 @@ var sizeLimits = []uint64{ 1<<(8*8) - 1, } +func bisectLeft(u uint64) int { + var n int + for sizeLimits[n] < u { + n++ + } + return n +} + func adjustFloatBytes(b []byte, encode bool) { if (encode && b[0]&0x80 != 0x00) || (!encode && b[0]&0x80 == 0x00) { // Negative numbers: flip all of the bytes. @@ -105,109 +115,128 @@ func adjustFloatBytes(b []byte, encode bool) { } } -func encodeBytes(buf *bytes.Buffer, code byte, b []byte) { - buf.WriteByte(code) - buf.Write(bytes.Replace(b, []byte{0x00}, []byte{0x00, 0xFF}, -1)) - buf.WriteByte(0x00) +type packer struct { + buf []byte } -func bisectLeft(u uint64) int { - var n int - for sizeLimits[n] < u { - n += 1 +func (p *packer) putByte(b byte) { + p.buf = append(p.buf, b) +} + +func (p *packer) putBytes(b []byte) { + p.buf = append(p.buf, b...) +} + +func (p *packer) putBytesNil(b []byte, i int) { + for i >= 0 { + p.putBytes(b[:i+1]) + p.putByte(0xFF) + b = b[i+1:] + i = bytes.IndexByte(b, 0x00) } - return n + p.putBytes(b) } -func encodeInt(buf *bytes.Buffer, i int64) { +func (p *packer) encodeBytes(code byte, b []byte) { + p.putByte(code) + if i := bytes.IndexByte(b, 0x00); i >= 0 { + p.putBytesNil(b, i) + } else { + p.putBytes(b) + } + p.putByte(0x00) +} + +func (p *packer) encodeInt(i int64) { if i == 0 { - buf.WriteByte(0x14) + p.putByte(0x14) return } var n int - var ibuf bytes.Buffer + var scratch [8]byte switch { case i > 0: n = bisectLeft(uint64(i)) - buf.WriteByte(byte(intZeroCode + n)) - binary.Write(&ibuf, binary.BigEndian, i) + p.putByte(byte(intZeroCode + n)) + binary.BigEndian.PutUint64(scratch[:], uint64(i)) case i < 0: n = bisectLeft(uint64(-i)) - buf.WriteByte(byte(0x14 - n)) - binary.Write(&ibuf, binary.BigEndian, int64(sizeLimits[n])+i) + p.putByte(byte(0x14 - n)) + offsetEncoded := int64(sizeLimits[n]) + i + binary.BigEndian.PutUint64(scratch[:], uint64(offsetEncoded)) } - buf.Write(ibuf.Bytes()[8-n:]) + p.putBytes(scratch[8-n:]) } -func encodeFloat(buf *bytes.Buffer, f float32) { - var ibuf bytes.Buffer - binary.Write(&ibuf, binary.BigEndian, f) - buf.WriteByte(floatCode) - out := ibuf.Bytes() - adjustFloatBytes(out, true) - buf.Write(out) +func (p *packer) encodeFloat(f float32) { + var scratch [4]byte + binary.BigEndian.PutUint32(scratch[:], math.Float32bits(f)) + adjustFloatBytes(scratch[:], true) + + p.putByte(floatCode) + p.putBytes(scratch[:]) } -func encodeDouble(buf *bytes.Buffer, d float64) { - var ibuf bytes.Buffer - binary.Write(&ibuf, binary.BigEndian, d) - buf.WriteByte(doubleCode) - out := ibuf.Bytes() - adjustFloatBytes(out, true) - buf.Write(out) +func (p *packer) encodeDouble(d float64) { + var scratch [8]byte + binary.BigEndian.PutUint64(scratch[:], math.Float64bits(d)) + adjustFloatBytes(scratch[:], true) + + p.putByte(doubleCode) + p.putBytes(scratch[:]) } -func encodeUUID(buf *bytes.Buffer, u UUID) { - buf.WriteByte(uuidCode) - buf.Write(u[:]) +func (p *packer) encodeUUID(u UUID) { + p.putByte(uuidCode) + p.putBytes(u[:]) } -func encodeTuple(buf *bytes.Buffer, t Tuple, nested bool) { +func (p *packer) encodeTuple(t Tuple, nested bool) { if nested { - buf.WriteByte(nestedCode) + p.putByte(nestedCode) } for i, e := range t { switch e := e.(type) { case Tuple: - encodeTuple(buf, e, true) + p.encodeTuple(e, true) case nil: - buf.WriteByte(nilCode) + p.putByte(nilCode) if nested { - buf.WriteByte(0xff) + p.putByte(0xff) } case int64: - encodeInt(buf, e) + p.encodeInt(e) case int: - encodeInt(buf, int64(e)) + p.encodeInt(int64(e)) case []byte: - encodeBytes(buf, bytesCode, e) + p.encodeBytes(bytesCode, e) case fdb.KeyConvertible: - encodeBytes(buf, bytesCode, []byte(e.FDBKey())) + p.encodeBytes(bytesCode, []byte(e.FDBKey())) case string: - encodeBytes(buf, stringCode, []byte(e)) + p.encodeBytes(stringCode, []byte(e)) case float32: - encodeFloat(buf, e) + p.encodeFloat(e) case float64: - encodeDouble(buf, e) + p.encodeDouble(e) case bool: if e { - buf.WriteByte(trueCode) + p.putByte(trueCode) } else { - buf.WriteByte(falseCode) + p.putByte(falseCode) } case UUID: - encodeUUID(buf, e) + p.encodeUUID(e) default: panic(fmt.Sprintf("unencodable element at index %d (%v, type %T)", i, t[i], t[i])) } } if nested { - buf.WriteByte(0x00) + p.putByte(0x00) } } @@ -220,9 +249,9 @@ func encodeTuple(buf *bytes.Buffer, t Tuple, nested bool) { // call Pack when using a Tuple with a FoundationDB API function that requires a // key. func (t Tuple) Pack() []byte { - buf := new(bytes.Buffer) - encodeTuple(buf, t, false) - return buf.Bytes() + p := packer{buf: make([]byte, 0, 64)} + p.encodeTuple(t, false) + return p.buf } func findTerminator(b []byte) int { @@ -356,7 +385,7 @@ func decodeTuple(b []byte, nested bool) (Tuple, int, error) { if err != nil { return nil, i, err } - off += 1 + off++ default: return nil, i, fmt.Errorf("unable to decode tuple element with unknown typecode %02x", b[i]) } diff --git a/bindings/go/src/fdb/tuple/tuple_test.go b/bindings/go/src/fdb/tuple/tuple_test.go new file mode 100644 index 00000000000..59c37bc7bc5 --- /dev/null +++ b/bindings/go/src/fdb/tuple/tuple_test.go @@ -0,0 +1,120 @@ +package tuple + +import ( + "bytes" + "encoding/gob" + "flag" + "math/rand" + "os" + "testing" +) + +var update = flag.Bool("update", false, "update .golden files") + +func loadGolden(t *testing.T) (golden map[string][]byte) { + f, err := os.Open("testdata/tuples.golden") + if err != nil { + t.Fatalf("failed to open golden file: %s", err) + } + defer f.Close() + + err = gob.NewDecoder(f).Decode(&golden) + if err != nil { + t.Fatalf("failed to decode golden file: %s", err) + } + return +} + +func writeGolden(t *testing.T, golden map[string][]byte) { + f, err := os.Create("testdata/tuples.golden") + if err != nil { + t.Fatalf("failed to open golden file: %s", err) + } + defer f.Close() + + err = gob.NewEncoder(f).Encode(golden) + if err != nil { + t.Fatalf("failed to encode golden file: %s", err) + } +} + +var testUUID = UUID{ + 0x11, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x11, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, +} + +func genBytes() interface{} { return []byte("namespace") } +func genBytesNil() interface{} { return []byte{0xFF, 0x00, 0xFF} } +func genString() interface{} { return "namespace" } +func genStringNil() interface{} { return "nam\x00es\xFFpace" } +func genInt() interface{} { return rand.Int63() } +func genFloat() interface{} { return float32(rand.NormFloat64()) } +func genDouble() interface{} { return rand.NormFloat64() } + +func mktuple(gen func() interface{}, count int) Tuple { + tt := make(Tuple, count) + for i := 0; i < count; i++ { + tt[i] = gen() + } + return tt +} + +var testCases = []struct { + name string + tuple Tuple +}{ + {"Simple", Tuple{testUUID, "foobarbaz", 1234, nil}}, + {"Namespaces", Tuple{testUUID, "github", "com", "apple", "foundationdb", "tree"}}, + {"ManyStrings", mktuple(genString, 8)}, + {"ManyStringsNil", mktuple(genStringNil, 8)}, + {"ManyBytes", mktuple(genBytes, 20)}, + {"ManyBytesNil", mktuple(genBytesNil, 20)}, + {"LargeBytes", Tuple{testUUID, bytes.Repeat([]byte("abcd"), 20)}}, + {"LargeBytesNil", Tuple{testUUID, bytes.Repeat([]byte{0xFF, 0x0, 0xFF}, 20)}}, + {"Integers", mktuple(genInt, 20)}, + {"Floats", mktuple(genFloat, 20)}, + {"Doubles", mktuple(genDouble, 20)}, + {"UUIDs", Tuple{testUUID, true, testUUID, false, testUUID, true, testUUID, false, testUUID, true}}, + {"NilCases", Tuple{"\x00", "\x00\xFF", "\x00\x00\x00", "\xFF\x00", ""}}, + {"Nested", Tuple{testUUID, mktuple(genInt, 4), nil, mktuple(genBytes, 4), nil, mktuple(genDouble, 4), nil}}, +} + +func TestTuplePacking(t *testing.T) { + var golden map[string][]byte + + if *update { + golden = make(map[string][]byte) + } else { + golden = loadGolden(t) + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + result := tt.tuple.Pack() + + if *update { + golden[tt.name] = result + return + } + + if !bytes.Equal(result, golden[tt.name]) { + t.Errorf("packing mismatch: expected %v, got %v", golden[tt.name], result) + } + }) + } + + if *update { + writeGolden(t, golden) + } +} + +func BenchmarkTuplePacking(b *testing.B) { + for _, bm := range testCases { + b.Run(bm.name, func(b *testing.B) { + tuple := bm.tuple + for i := 0; i < b.N; i++ { + _ = tuple.Pack() + } + }) + } +} diff --git a/bindings/java/local.mk b/bindings/java/local.mk index baa84309757..d91bf938049 100644 --- a/bindings/java/local.mk +++ b/bindings/java/local.mk @@ -140,7 +140,7 @@ ifeq ($(PLATFORM),linux) sed -e 's/-PRERELEASE/-SNAPSHOT/g' bindings/java/pom.xml > "$@" packages/fdb-java-$(JARVER).jar: fdb_java versions.target - @echo "Building $@" + @echo "Building $@" @rm -f $@ @rm -rf packages/jar_regular @mkdir -p packages/jar_regular diff --git a/bindings/java/pom.xml.in b/bindings/java/pom.xml.in index a5cdf28a47a..bde89709842 100644 --- a/bindings/java/pom.xml.in +++ b/bindings/java/pom.xml.in @@ -4,13 +4,13 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.apple.cie.foundationdb + org.foundationdb NAME VERSION jar foundationdb-java - Java bindings for the FoundationDB database. These bindings require the FoundationDB client, which is under a different license. The client can be obtained from https://www.foundationdb.org/downloads/fdb-c/. + Java bindings for the FoundationDB database. These bindings require the FoundationDB client, which is under a different license. The client can be obtained from https://www.foundationdb.org/download/. 2010 https://www.foundationdb.org @@ -18,13 +18,13 @@ FoundationDB https://www.foundationdb.org - + FoundationDB - + http://0.0.0.0 diff --git a/bindings/java/src/main/com/apple/foundationdb/FDBDatabase.java b/bindings/java/src/main/com/apple/foundationdb/FDBDatabase.java index 6fb9591b4eb..038aacb2e6a 100644 --- a/bindings/java/src/main/com/apple/foundationdb/FDBDatabase.java +++ b/bindings/java/src/main/com/apple/foundationdb/FDBDatabase.java @@ -45,7 +45,7 @@ public DatabaseOptions options() { @Override public T run(Function retryable, Executor e) { - Transaction t = this.createTransaction(); + Transaction t = this.createTransaction(e); try { while (true) { try { @@ -63,7 +63,7 @@ public T run(Function retryable, Executor e) { @Override public T read(Function retryable, Executor e) { - return this.run(retryable); + return this.run(retryable, e); } @Override diff --git a/bindings/java/src/main/com/apple/foundationdb/directory/DirectoryLayer.java b/bindings/java/src/main/com/apple/foundationdb/directory/DirectoryLayer.java index 681c8aa9249..be802b0cb66 100644 --- a/bindings/java/src/main/com/apple/foundationdb/directory/DirectoryLayer.java +++ b/bindings/java/src/main/com/apple/foundationdb/directory/DirectoryLayer.java @@ -1035,7 +1035,10 @@ public CompletableFuture chooseWindow(final Transaction tr, final HighCont Range oldAllocations = new Range(allocator.recent.getKey(), allocator.recent.get(windowStart).getKey()); CompletableFuture newCountRead; - // SOMEDAY: synchronize on something transaction local + // SOMEDAY: this code would work as written if synchronized on something transaction local. + // The reason we don't synchronize on the Transaction tr itself is that the user could also be using it + // for synchronization. If they did, then synchronizing on tr here could lead to performance issues or + // deadlocks. synchronized(HighContentionAllocator.class) { if(windowStart > initialWindowStart) { tr.clear(oldCounters); @@ -1073,7 +1076,10 @@ public CompletableFuture choosePrefix(final Transaction tr, final HighC AsyncIterable counterRange; CompletableFuture allocationTemp; - // SOMEDAY: synchronize on something transaction local + // SOMEDAY: this code would work as written if synchronized on something transaction local. + // The reason we don't synchronize on the Transaction tr itself is that the user could also be using it + // for synchronization. If they did, then synchronizing on tr here could lead to performance issues or + // deadlocks. synchronized(HighContentionAllocator.class) { counterRange = tr.snapshot().getRange(countersRange, 1, true); allocationTemp = tr.get(allocationKey); diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java b/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java index 8b63af55f75..50f023d5729 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java @@ -73,12 +73,12 @@ public class Tuple implements Comparable, Iterable { private List elements; private Tuple(List elements, Object newItem) { - this(new LinkedList(elements)); + this(elements); this.elements.add(newItem); } private Tuple(List elements) { - this.elements = new ArrayList(elements); + this.elements = new ArrayList<>(elements); } /** @@ -962,6 +962,14 @@ static void main(String[] args) { System.out.println("t2.getNestedTuple(17): " + t2.getNestedTuple(17)); System.out.println("t2.getVersionstamp(20): " + t2.getVersionstamp(20)); + int currOffset = 0; + for (Object item : t) { + int length = Tuple.from(item).pack().length; + Tuple t3 = Tuple.fromBytes(bytes, currOffset, length); + System.out.println("item = " + t3); + currOffset += length; + } + System.out.println("(2*(Long.MAX_VALUE+1),) = " + ByteArrayUtil.printable(Tuple.from( BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).shiftLeft(1) ).pack())); diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/TupleUtil.java b/bindings/java/src/main/com/apple/foundationdb/tuple/TupleUtil.java index 73291ed784f..cf1d337f2e8 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/TupleUtil.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/TupleUtil.java @@ -33,6 +33,8 @@ import java.util.UUID; import java.util.stream.Stream; +import com.apple.foundationdb.FDB; + class TupleUtil { private static final byte nil = 0x00; private static final BigInteger[] size_limits; @@ -537,10 +539,10 @@ static int compareItems(Object item1, Object item2) { } static List unpack(byte[] bytes, int start, int length) { - List items = new LinkedList(); + List items = new LinkedList<>(); int pos = start; int end = start + length; - while(pos < bytes.length) { + while(pos < end) { DecodeResult decoded = decode(bytes, pos, end); items.add(decoded.o); pos = decoded.end; @@ -569,7 +571,7 @@ static EncodeResult encodeAll(List items, byte[] prefix, List en } static byte[] pack(List items, byte[] prefix) { - List encoded = new ArrayList(2 * items.size() + (prefix == null ? 0 : 1)); + List encoded = new ArrayList<>(2 * items.size() + (prefix == null ? 0 : 1)); EncodeResult result = encodeAll(items, prefix, encoded); if(result.versionPos > 0) { throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple pack"); @@ -579,7 +581,7 @@ static byte[] pack(List items, byte[] prefix) { } static byte[] packWithVersionstamp(List items, byte[] prefix) { - List encoded = new ArrayList(2 * items.size() + (prefix == null ? 1 : 2)); + List encoded = new ArrayList<>(2 * items.size() + (prefix == null ? 1 : 2)); EncodeResult result = encodeAll(items, prefix, encoded); if(result.versionPos < 0) { throw new IllegalArgumentException("No incomplete Versionstamp included in tuple pack with versionstamp"); @@ -587,7 +589,11 @@ static byte[] packWithVersionstamp(List items, byte[] prefix) { if(result.versionPos > 0xffff) { throw new IllegalArgumentException("Tuple has incomplete version at position " + result.versionPos + " which is greater than the maximum " + 0xffff); } - encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array()); + if (FDB.instance().getAPIVersion() < 520) { + encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array()); + } else { + encoded.add(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(result.versionPos).array()); + } return ByteArrayUtil.join(null, encoded); } } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/AsyncDirectoryExtension.java b/bindings/java/src/test/com/apple/foundationdb/test/AsyncDirectoryExtension.java index 4fcf4f9f57f..9c2d56a9d1d 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/AsyncDirectoryExtension.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/AsyncDirectoryExtension.java @@ -170,7 +170,10 @@ else if(op == DirectoryOperation.DIRECTORY_LIST) { .thenAccept(children -> inst.push(Tuple.fromItems(children).pack())); } else if(op == DirectoryOperation.DIRECTORY_EXISTS) { - return inst.popParam() + // In Java, DirectoryLayer.exists can return true without doing any reads. + // Other bindings will always do a read, so we get a read version now to be compatible with that behavior. + return inst.readTcx.readAsync(tr -> tr.getReadVersion()) + .thenComposeAsync(v -> inst.popParam()) .thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count))) .thenComposeAsync(path -> { if(path.size() == 0) diff --git a/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java b/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java index 56d37ef1f26..a244b83cd87 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java @@ -317,8 +317,8 @@ else if(op == StackOperation.ON_ERROR) { if(t != null) { inst.context.newTransaction(oldTr); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior. } - else { - inst.setTransaction(oldTr, tr); + else if(!inst.setTransaction(oldTr, tr)) { + tr.close(); } }).thenApply(v -> null); @@ -479,7 +479,6 @@ else if(op == StackOperation.UNIT_TESTS) { tr.options().setReadYourWritesDisable(); tr.options().setReadSystemKeys(); tr.options().setAccessSystemKeys(); - tr.options().setDurabilityDevNullIsWebScale(); tr.options().setTimeout(60*1000); tr.options().setRetryLimit(50); tr.options().setMaxRetryDelay(100); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/Context.java b/bindings/java/src/test/com/apple/foundationdb/test/Context.java index a446aa2b10f..c71cb45f994 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/Context.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/Context.java @@ -92,6 +92,7 @@ public static synchronized void addTransactionReference(Transaction tr) { private static synchronized Transaction getTransaction(String trName) { Transaction tr = transactionMap.get(trName); + assert tr != null : "Null transaction"; addTransactionReference(tr); return tr; } @@ -117,7 +118,15 @@ private static synchronized void updateTransaction(String trName, Transaction tr } private static synchronized boolean updateTransaction(String trName, Transaction oldTr, Transaction newTr) { - if(transactionMap.replace(trName, oldTr, newTr)) { + boolean added; + if(oldTr == null) { + added = (transactionMap.putIfAbsent(trName, newTr) == null); + } + else { + added = transactionMap.replace(trName, oldTr, newTr); + } + + if(added) { addTransactionReference(newTr); releaseTransaction(oldTr); return true; diff --git a/bindings/java/src/test/com/apple/foundationdb/test/DirectoryExtension.java b/bindings/java/src/test/com/apple/foundationdb/test/DirectoryExtension.java index 8c2b3265b19..fbf91686697 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/DirectoryExtension.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/DirectoryExtension.java @@ -160,6 +160,11 @@ else if(op == DirectoryOperation.DIRECTORY_EXISTS) { int count = StackUtils.getInt(inst.popParam().get()); List> path = DirectoryUtil.popPaths(inst, count).get(); boolean exists; + + // In Java, DirectoryLayer.exists can return true without doing any reads. + // Other bindings will always do a read, so we get a read version now to be compatible with that behavior. + inst.readTcx.read(tr -> tr.getReadVersion().join()); + if(path.size() == 0) exists = directory().exists(inst.readTcx).get(); else diff --git a/bindings/java/src/test/com/apple/foundationdb/test/Instruction.java b/bindings/java/src/test/com/apple/foundationdb/test/Instruction.java index 86fe7135543..d991217a0ad 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/Instruction.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/Instruction.java @@ -72,16 +72,21 @@ else if(isSnapshot) { readTcx = isDatabase ? context.db : readTr; } - void setTransaction(Transaction newTr) { + boolean setTransaction(Transaction newTr) { if(!isDatabase) { context.updateCurrentTransaction(newTr); + return true; } + + return false; } - void setTransaction(Transaction oldTr, Transaction newTr) { + boolean setTransaction(Transaction oldTr, Transaction newTr) { if(!isDatabase) { - context.updateCurrentTransaction(oldTr, newTr); + return context.updateCurrentTransaction(oldTr, newTr); } + + return false; } void releaseTransaction() { diff --git a/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java b/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java index e6f62f07d26..96281dec72f 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java @@ -284,7 +284,10 @@ else if(op == StackOperation.ON_ERROR) { FDBException err = new FDBException("Fake testing error", filteredError ? 1020 : errorCode); try { - inst.setTransaction(inst.tr.onError(err).join()); + Transaction tr = inst.tr.onError(err).join(); + if(!inst.setTransaction(tr)) { + tr.close(); + } } catch(Throwable t) { inst.context.newTransaction(); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior. @@ -429,7 +432,6 @@ else if(op == StackOperation.UNIT_TESTS) { tr.options().setReadYourWritesDisable(); tr.options().setReadSystemKeys(); tr.options().setAccessSystemKeys(); - tr.options().setDurabilityDevNullIsWebScale(); tr.options().setTimeout(60*1000); tr.options().setRetryLimit(50); tr.options().setMaxRetryDelay(100); diff --git a/bindings/python/README.rst b/bindings/python/README.rst index 168b522f080..d3e2d696048 100644 --- a/bindings/python/README.rst +++ b/bindings/python/README.rst @@ -1,3 +1,3 @@ -Complete documentation of the FoundationDB Python API can be found at https://www.foundationdb.org/documentation/api-python.html. +Complete documentation of the FoundationDB Python API can be found at https://apple.github.io/foundationdb/api-python.html. -These bindings require the FoundationDB client. The client can be obtained from https://www.foundationdb.org/downloads/fdb-c/. +These bindings require the FoundationDB client. The client can be obtained from https://www.foundationdb.org/download/. diff --git a/bindings/python/fdb/__init__.py b/bindings/python/fdb/__init__.py index 16347250b55..978290e51c8 100644 --- a/bindings/python/fdb/__init__.py +++ b/bindings/python/fdb/__init__.py @@ -21,7 +21,7 @@ # FoundationDB Python API """Documentation for this API can be found at -https://www.foundationdb.org/documentation/api-python.html""" +https://apple.github.io/foundationdb/api-python.html""" def open(*args, **kwargs): diff --git a/bindings/python/fdb/locality.py b/bindings/python/fdb/locality.py index cc298c00c87..d6f6e152010 100644 --- a/bindings/python/fdb/locality.py +++ b/bindings/python/fdb/locality.py @@ -21,7 +21,7 @@ # FoundationDB Python API """Documentation for this API can be found at -https://www.foundationdb.org/documentation/api-python.html""" +https://apple.github.io/foundationdb/api-python.html""" from fdb import impl as _impl diff --git a/bindings/python/fdb/tuple.py b/bindings/python/fdb/tuple.py index 766e6590852..f9092e0b6a4 100644 --- a/bindings/python/fdb/tuple.py +++ b/bindings/python/fdb/tuple.py @@ -363,7 +363,7 @@ def _encode(value, nested=False): elif isinstance(value, tuple) or isinstance(value, list): child_bytes, version_pos = _reduce_children(map(lambda x: _encode(x, True), value)) new_version_pos = -1 if version_pos < 0 else version_pos + 1 - return b''.join([six.int2byte(NESTED_CODE)] + child_bytes + [six.int2byte(0x00)]), version_pos + return b''.join([six.int2byte(NESTED_CODE)] + child_bytes + [six.int2byte(0x00)]), new_version_pos else: raise ValueError("Unsupported data type: " + str(type(value))) @@ -384,7 +384,10 @@ def _pack_maybe_with_versionstamp(t, prefix=None): if version_pos >= 0: version_pos += len(prefix) if prefix is not None else 0 bytes_list.extend(child_bytes) - bytes_list.append(struct.pack('" - print('changed directory to %d (%s @%s)' % (self.dir_index, clazz, new_path)) + print('changed directory to %d (%s @%r)' % (self.dir_index, clazz, new_path)) elif inst.op == six.u('DIRECTORY_SET_ERROR_INDEX'): self.error_index = inst.pop() elif inst.op == six.u('DIRECTORY_CREATE_OR_OPEN'): path = self.pop_tuples(inst.stack) layer = inst.pop() - log_op('create_or_open %s: layer=%s' % (repr(directory.get_path() + path), repr(layer))) + log_op('create_or_open %r: layer=%r' % (directory.get_path() + path, layer)) d = directory.create_or_open(inst.tr, path, layer or b'') self.append_dir(inst, d) elif inst.op == six.u('DIRECTORY_CREATE'): path = self.pop_tuples(inst.stack) layer, prefix = inst.pop(2) - log_op('create %s: layer=%s, prefix=%s' % (repr(directory.get_path() + path), repr(layer), repr(prefix))) + log_op('create %r: layer=%r, prefix=%r' % (directory.get_path() + path, layer, prefix)) self.append_dir(inst, directory.create(inst.tr, path, layer or b'', prefix)) elif inst.op == six.u('DIRECTORY_OPEN'): path = self.pop_tuples(inst.stack) layer = inst.pop() - log_op('open %s: layer=%s' % (repr(directory.get_path() + path), repr(layer))) + log_op('open %r: layer=%r' % (directory.get_path() + path, layer)) self.append_dir(inst, directory.open(inst.tr, path, layer or b'')) elif inst.op == six.u('DIRECTORY_MOVE'): old_path, new_path = self.pop_tuples(inst.stack, 2) - log_op('move %s to %s' % (repr(directory.get_path() + old_path), repr(directory.get_path() + new_path))) + log_op('move %r to %r' % (directory.get_path() + old_path, directory.get_path() + new_path)) self.append_dir(inst, directory.move(inst.tr, old_path, new_path)) elif inst.op == six.u('DIRECTORY_MOVE_TO'): new_absolute_path = self.pop_tuples(inst.stack) - log_op('move %s to %s' % (repr(directory.get_path()), repr(new_absolute_path))) + log_op('move %r to %r' % (directory.get_path(), new_absolute_path)) self.append_dir(inst, directory.move_to(inst.tr, new_absolute_path)) elif inst.op == six.u('DIRECTORY_REMOVE'): count = inst.pop() if count == 0: - log_op('remove %s' % repr(directory.get_path())) + log_op('remove %r' % (directory.get_path(),)) directory.remove(inst.tr) else: path = self.pop_tuples(inst.stack) - log_op('remove %s' % repr(directory.get_path() + path)) + log_op('remove %r' % (directory.get_path() + path,)) directory.remove(inst.tr, path) elif inst.op == six.u('DIRECTORY_REMOVE_IF_EXISTS'): count = inst.pop() if count == 0: - log_op('remove_if_exists %s' % repr(directory.get_path())) + log_op('remove_if_exists %r' % (directory.get_path(),)) directory.remove_if_exists(inst.tr) else: path = self.pop_tuples(inst.stack) - log_op('remove_if_exists %s' % repr(directory.get_path() + path)) + log_op('remove_if_exists %r' % (directory.get_path() + path,)) directory.remove_if_exists(inst.tr, path) elif inst.op == six.u('DIRECTORY_LIST'): count = inst.pop() if count == 0: result = directory.list(inst.tr) - log_op('list %s' % (repr(directory.get_path()))) + log_op('list %r' % (directory.get_path(),)) else: path = self.pop_tuples(inst.stack) result = directory.list(inst.tr, path) - log_op('list %s' % (repr(directory.get_path() + path))) + log_op('list %r' % (directory.get_path() + path,)) inst.push(fdb.tuple.pack(tuple(result))) elif inst.op == six.u('DIRECTORY_EXISTS'): count = inst.pop() if count == 0: result = directory.exists(inst.tr) - log_op('exists %s: %d' % (repr(directory.get_path()), result)) + log_op('exists %r: %d' % (directory.get_path(), result)) else: path = self.pop_tuples(inst.stack) result = directory.exists(inst.tr, path) - log_op('exists %s: %d' % (repr(directory.get_path() + path), result)) + log_op('exists %r: %d' % (directory.get_path() + path, result)) if result: inst.push(1) @@ -177,7 +177,7 @@ def process_instruction(self, inst): inst.push(directory.pack(key_tuple)) elif inst.op == six.u('DIRECTORY_UNPACK_KEY'): key = inst.pop() - log_op('unpack %s in subspace with prefix %s' % (repr(key), repr(directory.rawPrefix))) + log_op('unpack %r in subspace with prefix %r' % (key, directory.rawPrefix)) tup = directory.unpack(key) for t in tup: inst.push(t) @@ -215,7 +215,7 @@ def process_instruction(self, inst): elif inst.op == six.u('DIRECTORY_STRIP_PREFIX'): s = inst.pop() if not s.startswith(directory.key()): - raise Exception('String %s does not start with raw prefix %s' % (s, directory.key())) + raise Exception('String %r does not start with raw prefix %r' % (s, directory.key())) inst.push(s[len(directory.key()):]) else: diff --git a/bindings/python/tests/tester.py b/bindings/python/tests/tester.py old mode 100755 new mode 100644 index 79e7074d503..6aefe7ebd7e --- a/bindings/python/tests/tester.py +++ b/bindings/python/tests/tester.py @@ -32,14 +32,7 @@ sys.path[:0] = [os.path.join(os.path.dirname(__file__), '..')] import fdb - -assert not fdb.is_api_version_selected() -try: - fdb.get_api_version() -except RuntimeError as e: - assert str(e) == 'API version is not set' fdb.api_version(int(sys.argv[2])) -assert int(sys.argv[2]) == fdb.get_api_version() from fdb import six from fdb.impl import strinc @@ -98,7 +91,7 @@ def pop(self, count=None, with_idx=False): else: raw[i] = (raw[i][0], val) except fdb.FDBError as e: - # print('ERROR: %s' % repr(e)) + # print('ERROR: %r' % e) raw[i] = (raw[i][0], fdb.tuple.pack((b'ERROR', str(e.code).encode('ascii')))) if count is None: @@ -138,7 +131,6 @@ def test_options(tr): tr.options.set_read_your_writes_disable() tr.options.set_read_system_keys() tr.options.set_access_system_keys() - tr.options.set_durability_dev_null_is_web_scale() tr.options.set_timeout(60 * 1000) tr.options.set_retry_limit(50) tr.options.set_max_retry_delay(100) @@ -529,19 +521,6 @@ def run(self): Tester.wait_empty(self.db, prefix) inst.push(b"WAITED_FOR_EMPTY") elif inst.op == six.u("UNIT_TESTS"): - assert fdb.is_api_version_selected() - api_version = fdb.get_api_version() - try: - fdb.api_version(api_version + 1) - raise RuntimeError('Was not stopped from selecting two API versions') - except RuntimeError as e: - assert str(e) == 'FDB API already loaded at version {}'.format(api_version) - try: - fdb.api_version(api_version - 1) - raise RuntimeError('Was not stopped from selecting two API versions') - except RuntimeError as e: - assert str(e) == 'FDB API already loaded at version {}'.format(api_version) - fdb.api_version(api_version) try: db.options.set_location_cache_size(100001) @@ -564,7 +543,7 @@ def run(self): else: raise Exception("Unknown op %s" % inst.op) except fdb.FDBError as e: - # print('ERROR: %s' % repr(e)) + # print('ERROR: %r' % e) inst.stack.push(idx, fdb.tuple.pack((b"ERROR", str(e.code).encode('ascii')))) # print(" to %s" % self.stack) diff --git a/bindings/ruby/fdb.gemspec.in b/bindings/ruby/fdb.gemspec.in index ad3cfbbeab1..76553e975b1 100644 --- a/bindings/ruby/fdb.gemspec.in +++ b/bindings/ruby/fdb.gemspec.in @@ -9,7 +9,7 @@ Gem::Specification.new do |s| Ruby bindings for the FoundationDB database. Complete documentation of the FoundationDB Ruby API can be found at: -https://www.foundationdb.org/documentation/api-ruby.html. +https://apple.github.io/foundationdb/api-ruby.html. EOF s.authors = ["FoundationDB"] s.email = 'fdb-dist@apple.com' @@ -18,5 +18,5 @@ EOF s.license = 'Apache v2' s.add_dependency('ffi', '>= 1.1.5') s.required_ruby_version = '>= 1.9.3' - s.requirements << 'These bindings require the FoundationDB client. The client can be obtained from https://www.foundationdb.org/downloads/fdb-c/.' + s.requirements << 'These bindings require the FoundationDB client. The client can be obtained from https://www.foundationdb.org/download/.' end diff --git a/bindings/ruby/lib/fdb.rb b/bindings/ruby/lib/fdb.rb index 7c13847a450..8a87b82b3bb 100644 --- a/bindings/ruby/lib/fdb.rb +++ b/bindings/ruby/lib/fdb.rb @@ -21,7 +21,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html module FDB @@chosen_version = -1 diff --git a/bindings/ruby/lib/fdbdirectory.rb b/bindings/ruby/lib/fdbdirectory.rb index 5c411fe91e3..d14af34722b 100644 --- a/bindings/ruby/lib/fdbdirectory.rb +++ b/bindings/ruby/lib/fdbdirectory.rb @@ -23,7 +23,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html require 'thread' diff --git a/bindings/ruby/lib/fdbimpl.rb b/bindings/ruby/lib/fdbimpl.rb index dcee3236b98..565bc915b9a 100644 --- a/bindings/ruby/lib/fdbimpl.rb +++ b/bindings/ruby/lib/fdbimpl.rb @@ -23,7 +23,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html require 'ffi' diff --git a/bindings/ruby/lib/fdblocality.rb b/bindings/ruby/lib/fdblocality.rb index b661bfd0715..400769adbb3 100644 --- a/bindings/ruby/lib/fdblocality.rb +++ b/bindings/ruby/lib/fdblocality.rb @@ -23,7 +23,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html module FDB module Locality diff --git a/bindings/ruby/lib/fdbsubspace.rb b/bindings/ruby/lib/fdbsubspace.rb index 3ff7444877d..eab191330a9 100644 --- a/bindings/ruby/lib/fdbsubspace.rb +++ b/bindings/ruby/lib/fdbsubspace.rb @@ -23,7 +23,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html require_relative 'fdbtuple' diff --git a/bindings/ruby/lib/fdbtuple.rb b/bindings/ruby/lib/fdbtuple.rb index 816d28e1fc2..71b2402693a 100644 --- a/bindings/ruby/lib/fdbtuple.rb +++ b/bindings/ruby/lib/fdbtuple.rb @@ -23,7 +23,7 @@ # FoundationDB Ruby API # Documentation for this API can be found at -# https://www.foundationdb.org/documentation/api-ruby.html +# https://apple.github.io/foundationdb/api-ruby.html module FDB module Tuple diff --git a/bindings/ruby/tests/tester.rb b/bindings/ruby/tests/tester.rb old mode 100755 new mode 100644 index d241bad0c37..e77e33304b5 --- a/bindings/ruby/tests/tester.rb +++ b/bindings/ruby/tests/tester.rb @@ -462,7 +462,6 @@ def run tr.options.set_read_your_writes_disable tr.options.set_read_system_keys tr.options.set_access_system_keys - tr.options.set_durability_dev_null_is_web_scale tr.options.set_timeout(60*1000) tr.options.set_retry_limit(50) tr.options.set_max_retry_delay(100) diff --git a/build/packages.mk b/build/packages.mk index 36f10dcc9d2..425fd095267 100644 --- a/build/packages.mk +++ b/build/packages.mk @@ -121,7 +121,9 @@ ifeq ($(PLATFORM),linux) @rm -f packages/foundationdb-server-*.rpm packages/foundationdb-clients-*.rpm @packaging/rpm/buildrpms.sh $(VERSION) $(PKGRELEASE) - packages: DEB RPM JAVA_RELEASE FDBTESTS FDBHEADERS + FDBTLS: bin/fdb-libressl-plugin.$(DLEXT) + + packages: DEB RPM JAVA_RELEASE FDBTESTS FDBHEADERS FDBTLS packages_clean: DEB_clean RPM_clean JAVA_RELEASE_clean FDBHEADERS_clean diff --git a/documentation/StatusSchema.json b/documentation/StatusSchema.json index 8dbc2557001..f6638e59d35 100644 --- a/documentation/StatusSchema.json +++ b/documentation/StatusSchema.json @@ -1,461 +1,474 @@ -{ - "cluster":{ - "layers":{ - "_valid":true, - "_error":"some error description" - }, - "processes":{ - "$map":{ - "version":"3.0.0", - "machine_id":"0ccb4e0feddb5583010f6b77d9d10ece", - "locality":{ - "$map":"value" - }, - "class_source":{ - "$enum":[ - "command_line", - "configure_auto", - "set_class" - ] - }, - "class_type":{ - "$enum":[ - "unset", - "storage", - "transaction", - "resolution", - "proxy", - "master", - "test" - ] - }, - "roles":[ - { - "query_queue_max":0, - "input_bytes":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - }, - "kvstore_used_bytes":12341234, - "stored_bytes":12341234, - "kvstore_free_bytes":12341234, - "durable_bytes":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - }, - "queue_disk_free_bytes":12341234, - "persistent_disk_used_bytes":12341234, - "role":{ - "$enum":[ - "master", - "proxy", - "log", - "storage", - "resolver", - "cluster_controller" - ] - }, - "data_version":12341234, - "data_version_lag":12341234, - "persistent_disk_total_bytes":12341234, - "queue_disk_total_bytes":12341234, - "persistent_disk_free_bytes":12341234, - "queue_disk_used_bytes":12341234, - "id":"eb84471d68c12d1d26f692a50000003f", - "kvstore_total_bytes":12341234, - "finished_queries":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - } - } - ], - "command_line":"-r simulation", - "memory":{ - "available_bytes":0, - "limit_bytes":0, - "used_bytes":0 - }, - "messages":[ - { - "time":12345.12312, - "type":"x", - "name":{ - "$enum":[ - "file_open_error", - "incorrect_cluster_file_contents", - "process_error", - "io_error", - "io_timeout", - "platform_error", - "storage_server_lagging", - "(other FDB error messages)" - ] - }, - "raw_log_message":"", - "description":"abc" - } - ], - "fault_domain":"0ccb4e0fdbdb5583010f6b77d9d10ece", - "excluded":false, - "address":"1.2.3.4:1234", - "disk":{ - "free_bytes":3451233456234, - "reads":{ - "hz":0.0, - "counter":0, - "sectors":0 - }, - "busy":0.0, - "writes":{ - "hz":0.0, - "counter":0, - "sectors":0 - }, - "total_bytes":123412341234 - }, - "uptime_seconds":1234.2345, - "cpu":{ - "usage_cores":0.0 - }, - "network":{ - "current_connections":0, - "connections_established":{ - "hz":0.0 - }, - "connections_closed":{ - "hz":0.0 - }, - "connection_errors":{ - "hz":0.0 - }, - "megabits_sent":{ - "hz":0.0 - }, - "megabits_received":{ - "hz":0.0 - } - } - } - }, - "old_logs":[ - { - "logs":[ - { - "id":"7f8d623d0cb9966e", - "healthy":true, - "address":"1.2.3.4:1234" - } - ], - "log_replication_factor":3, - "log_write_anti_quorum":0, - "log_fault_tolerance":2 - } - ], - "fault_tolerance":{ - "max_machine_failures_without_losing_availability":0, - "max_machine_failures_without_losing_data":0 - }, - "qos":{ - "worst_queue_bytes_log_server":460, - "performance_limited_by":{ - "reason_server_id":"7f8d623d0cb9966e", - "reason_id":0, - "name":{ - "$enum":[ - "workload", - "storage_server_write_queue_size", - "storage_server_write_bandwidth_mvcc", - "storage_server_readable_behind", - "log_server_mvcc_write_bandwidth", - "log_server_write_queue", - "storage_server_min_free_space", - "storage_server_min_free_space_ratio", - "log_server_min_free_space", - "log_server_min_free_space_ratio" - ] - }, - "description":"The database is not being saturated by the workload." - }, - "transactions_per_second_limit":0, - "released_transactions_per_second":0, - "limiting_queue_bytes_storage_server":0, - "worst_queue_bytes_storage_server":0, - "limiting_version_lag_storage_server":0, - "worst_version_lag_storage_server":0 - }, - "incompatible_connections":[ - - ], - "database_available":true, - "database_locked":false, - "generation":2, - "latency_probe":{ - "read_seconds":7, - "immediate_priority_transaction_start_seconds":0.0, - "batch_priority_transaction_start_seconds":0.0, - "transaction_start_seconds":0.0, - "commit_seconds":0.02 - }, - "clients":{ - "count":1, - "supported_versions":[ - { - "client_version":"3.0.0", - "connected_clients":[ - { - "address":"127.0.0.1:9898", - "log_group":"default" - } - ], - "count" : 1, - "protocol_version" : "fdb00a400050001", - "source_version" : "9430e1127b4991cbc5ab2b17f41cfffa5de07e9d" - } - ] - }, - "messages":[ - { - "reasons":[ - { - "description":"Blah." - } - ], - "unreachable_processes":[ - { - "address":"1.2.3.4:1234" - } - ], - "name":{ - "$enum":[ - "unreachable_master_worker", - "unreadable_configuration", - "client_issues", - "unreachable_processes", - "immediate_priority_transaction_start_probe_timeout", - "batch_priority_transaction_start_probe_timeout", - "transaction_start_probe_timeout", - "read_probe_timeout", - "commit_probe_timeout", - "storage_servers_error", - "status_incomplete", - "layer_status_incomplete", - "database_availability_timeout" - ] - }, - "issues":[ - { - "name":{ - "$enum":[ - "incorrect_cluster_file_contents" - ] - }, - "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." - } - ], - "description":"abc" - } - ], - "recovery_state":{ - "required_resolvers":1, - "required_proxies":1, - "name":{ - "$enum":[ - "reading_coordinated_state", - "locking_coordinated_state", - "locking_old_transaction_servers", - "reading_transaction_system_state", - "configuration_missing", - "configuration_never_created", - "configuration_invalid", - "recruiting_transaction_servers", - "initializing_transaction_servers", - "recovery_transaction", - "writing_coordinated_state", - "fully_recovered" - ] - }, - "required_logs":3, - "missing_logs":"7f8d623d0cb9966e", - "description":"Recovery complete." - }, - "workload":{ - "operations":{ - "writes":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - }, - "reads":{ - "hz":0.0 - } - }, - "bytes":{ - "written":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - } - }, - "transactions":{ - "started":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - }, - "conflicted":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - }, - "committed":{ - "hz":0.0, - "counter":0, - "roughness":0.0 - } - } - }, - "cluster_controller_timestamp":1415650089, - "protocol_version":"fdb00a400050001", - "configuration":{ - "resolvers":1, - "redundancy":{ - "factor":{ - "$enum":[ - "single", - "double", - "triple", - "custom", - "two_datacenter", - "three_datacenter", - "three_data_hall", - "fast_recovery_double", - "fast_recovery_triple" - ] - } - }, - "storage_policy":"(zoneid^3x1)", - "tlog_policy":"(zoneid^2x1)", - "logs":2, - "storage_engine":{ - "$enum":[ - "ssd", - "ssd-1", - "ssd-2", - "memory", - "custom" - ] - }, - "coordinators_count":1, - "excluded_servers":[ - { - "address":"10.0.4.1" - } - ], - "proxies":5 - }, - "data":{ - "least_operating_space_bytes_log_server":0, - "average_partition_size_bytes":0, - "state":{ - "healthy":true, - "min_replicas_remaining":0, - "name":{ - "$enum":[ - "initializing", - "missing_data", - "healing", - "healthy_repartitioning", - "healthy_removing_server", - "healthy_rebalancing", - "healthy" - ] - }, - "description":"" - }, - "least_operating_space_ratio_storage_server":0.1, - "max_machine_failures_without_losing_availability":0, - "total_disk_used_bytes":0, - "total_kv_size_bytes":0, - "partitions_count":2, - "moving_data":{ - "total_written_bytes":0, - "in_flight_bytes":0, - "in_queue_bytes":0 - }, - "least_operating_space_bytes_storage_server":0, - "max_machine_failures_without_losing_data":0 - }, - "machines":{ - "$map":{ - "network":{ - "megabits_sent":{ - "hz":0.0 - }, - "megabits_received":{ - "hz":0.0 - }, - "tcp_segments_retransmitted":{ - "hz":0.0 - } - }, - "memory":{ - "free_bytes":0, - "committed_bytes":0, - "total_bytes":0 - }, - "contributing_workers":4, - "datacenter_id":"6344abf1813eb05b", - "excluded":false, - "address":"1.2.3.4", - "machine_id":"6344abf1813eb05b", - "locality":{ - "$map":"value" - }, - "cpu":{ - "logical_core_utilization":0.4 - } - } - } - }, - "client":{ - "coordinators":{ - "coordinators":[ - { - "reachable":true, - "address":"127.0.0.1:4701" - } - ], - "quorum_reachable":true - }, - "database_status":{ - "available":true, - "healthy":true - }, - "messages":[ - { - "name":{ - "$enum":[ - "inconsistent_cluster_file", - "unreachable_cluster_controller", - "no_cluster_controller", - "status_incomplete_client", - "status_incomplete_coordinators", - "status_incomplete_error", - "status_incomplete_timeout", - "status_incomplete_cluster", - "quorum_not_reachable" - ] - }, - "description":"The cluster file is not up to date." - } - ], - "timestamp":1415650089, - "cluster_file":{ - "path":"/etc/foundationdb/fdb.cluster", - "up_to_date":true - } - } -} +{ + "cluster":{ + "layers":{ + "_valid":true, + "_error":"some error description" + }, + "processes":{ + "$map":{ + "version":"3.0.0", + "machine_id":"0ccb4e0feddb5583010f6b77d9d10ece", + "locality":{ + "$map":"value" + }, + "class_source":{ + "$enum":[ + "command_line", + "configure_auto", + "set_class" + ] + }, + "class_type":{ + "$enum":[ + "unset", + "storage", + "transaction", + "resolution", + "proxy", + "master", + "test" + ] + }, + "roles":[ + { + "query_queue_max":0, + "input_bytes":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "stored_bytes":12341234, + "kvstore_used_bytes":12341234, + "kvstore_available_bytes":12341234, + "kvstore_free_bytes":12341234, + "kvstore_total_bytes":12341234, + "durable_bytes":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "queue_disk_used_bytes":12341234, + "queue_disk_available_bytes":12341234, + "queue_disk_free_bytes":12341234, + "queue_disk_total_bytes":12341234, + "role":{ + "$enum":[ + "master", + "proxy", + "log", + "storage", + "resolver", + "cluster_controller" + ] + }, + "data_version":12341234, + "data_version_lag":12341234, + "id":"eb84471d68c12d1d26f692a50000003f", + "finished_queries":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + } + } + ], + "command_line":"-r simulation", + "memory":{ + "available_bytes":0, + "limit_bytes":0, + "used_bytes":0 + }, + "messages":[ + { + "time":12345.12312, + "type":"x", + "name":{ + "$enum":[ + "file_open_error", + "incorrect_cluster_file_contents", + "process_error", + "io_error", + "io_timeout", + "platform_error", + "storage_server_lagging", + "(other FDB error messages)" + ] + }, + "raw_log_message":"", + "description":"abc" + } + ], + "fault_domain":"0ccb4e0fdbdb5583010f6b77d9d10ece", + "excluded":false, + "address":"1.2.3.4:1234", + "disk":{ + "free_bytes":3451233456234, + "reads":{ + "hz":0.0, + "counter":0, + "sectors":0 + }, + "busy":0.0, + "writes":{ + "hz":0.0, + "counter":0, + "sectors":0 + }, + "total_bytes":123412341234 + }, + "uptime_seconds":1234.2345, + "cpu":{ + "usage_cores":0.0 + }, + "network":{ + "current_connections":0, + "connections_established":{ + "hz":0.0 + }, + "connections_closed":{ + "hz":0.0 + }, + "connection_errors":{ + "hz":0.0 + }, + "megabits_sent":{ + "hz":0.0 + }, + "megabits_received":{ + "hz":0.0 + } + } + } + }, + "old_logs":[ + { + "logs":[ + { + "id":"7f8d623d0cb9966e", + "healthy":true, + "address":"1.2.3.4:1234" + } + ], + "log_replication_factor":3, + "log_write_anti_quorum":0, + "log_fault_tolerance":2 + } + ], + "fault_tolerance":{ + "max_machine_failures_without_losing_availability":0, + "max_machine_failures_without_losing_data":0 + }, + "qos":{ + "worst_queue_bytes_log_server":460, + "performance_limited_by":{ + "reason_server_id":"7f8d623d0cb9966e", + "reason_id":0, + "name":{ + "$enum":[ + "workload", + "storage_server_write_queue_size", + "storage_server_write_bandwidth_mvcc", + "storage_server_readable_behind", + "log_server_mvcc_write_bandwidth", + "log_server_write_queue", + "storage_server_min_free_space", + "storage_server_min_free_space_ratio", + "log_server_min_free_space", + "log_server_min_free_space_ratio" + ] + }, + "description":"The database is not being saturated by the workload." + }, + "transactions_per_second_limit":0, + "released_transactions_per_second":0, + "limiting_queue_bytes_storage_server":0, + "worst_queue_bytes_storage_server":0, + "limiting_version_lag_storage_server":0, + "worst_version_lag_storage_server":0 + }, + "incompatible_connections":[ + + ], + "database_available":true, + "database_locked":false, + "generation":2, + "latency_probe":{ + "read_seconds":7, + "immediate_priority_transaction_start_seconds":0.0, + "batch_priority_transaction_start_seconds":0.0, + "transaction_start_seconds":0.0, + "commit_seconds":0.02 + }, + "clients":{ + "count":1, + "supported_versions":[ + { + "client_version":"3.0.0", + "connected_clients":[ + { + "address":"127.0.0.1:9898", + "log_group":"default" + } + ], + "count" : 1, + "protocol_version" : "fdb00a400050001", + "source_version" : "9430e1127b4991cbc5ab2b17f41cfffa5de07e9d" + } + ] + }, + "messages":[ + { + "reasons":[ + { + "description":"Blah." + } + ], + "unreachable_processes":[ + { + "address":"1.2.3.4:1234" + } + ], + "name":{ + "$enum":[ + "unreachable_master_worker", + "unreadable_configuration", + "client_issues", + "unreachable_processes", + "immediate_priority_transaction_start_probe_timeout", + "batch_priority_transaction_start_probe_timeout", + "transaction_start_probe_timeout", + "read_probe_timeout", + "commit_probe_timeout", + "storage_servers_error", + "status_incomplete", + "layer_status_incomplete", + "database_availability_timeout" + ] + }, + "issues":[ + { + "name":{ + "$enum":[ + "incorrect_cluster_file_contents" + ] + }, + "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." + } + ], + "description":"abc" + } + ], + "recovery_state":{ + "required_resolvers":1, + "required_proxies":1, + "name":{ + "$enum":[ + "reading_coordinated_state", + "locking_coordinated_state", + "locking_old_transaction_servers", + "reading_transaction_system_state", + "configuration_missing", + "configuration_never_created", + "configuration_invalid", + "recruiting_transaction_servers", + "initializing_transaction_servers", + "recovery_transaction", + "writing_coordinated_state", + "fully_recovered" + ] + }, + "required_logs":3, + "missing_logs":"7f8d623d0cb9966e", + "description":"Recovery complete." + }, + "workload":{ + "operations":{ + "writes":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "reads":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + } + }, + "bytes":{ + "written":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "read":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + } + }, + "keys":{ + "read":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + } + }, + "transactions":{ + "started":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "conflicted":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "committed":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + } + } + }, + "cluster_controller_timestamp":1415650089, + "protocol_version":"fdb00a400050001", + "configuration":{ + "resolvers":1, + "redundancy":{ + "factor":{ + "$enum":[ + "single", + "double", + "triple", + "custom", + "two_datacenter", + "three_datacenter", + "three_data_hall", + "fast_recovery_double", + "fast_recovery_triple" + ] + } + }, + "storage_policy":"(zoneid^3x1)", + "tlog_policy":"(zoneid^2x1)", + "logs":2, + "storage_engine":{ + "$enum":[ + "ssd", + "ssd-1", + "ssd-2", + "memory", + "custom" + ] + }, + "coordinators_count":1, + "excluded_servers":[ + { + "address":"10.0.4.1" + } + ], + "proxies":5 + }, + "data":{ + "least_operating_space_bytes_log_server":0, + "average_partition_size_bytes":0, + "state":{ + "healthy":true, + "min_replicas_remaining":0, + "name":{ + "$enum":[ + "initializing", + "missing_data", + "healing", + "healthy_repartitioning", + "healthy_removing_server", + "healthy_rebalancing", + "healthy" + ] + }, + "description":"" + }, + "least_operating_space_ratio_storage_server":0.1, + "max_machine_failures_without_losing_availability":0, + "total_disk_used_bytes":0, + "total_kv_size_bytes":0, + "partitions_count":2, + "moving_data":{ + "total_written_bytes":0, + "in_flight_bytes":0, + "in_queue_bytes":0 + }, + "least_operating_space_bytes_storage_server":0, + "max_machine_failures_without_losing_data":0 + }, + "machines":{ + "$map":{ + "network":{ + "megabits_sent":{ + "hz":0.0 + }, + "megabits_received":{ + "hz":0.0 + }, + "tcp_segments_retransmitted":{ + "hz":0.0 + } + }, + "memory":{ + "free_bytes":0, + "committed_bytes":0, + "total_bytes":0 + }, + "contributing_workers":4, + "datacenter_id":"6344abf1813eb05b", + "excluded":false, + "address":"1.2.3.4", + "machine_id":"6344abf1813eb05b", + "locality":{ + "$map":"value" + }, + "cpu":{ + "logical_core_utilization":0.4 + } + } + } + }, + "client":{ + "coordinators":{ + "coordinators":[ + { + "reachable":true, + "address":"127.0.0.1:4701" + } + ], + "quorum_reachable":true + }, + "database_status":{ + "available":true, + "healthy":true + }, + "messages":[ + { + "name":{ + "$enum":[ + "inconsistent_cluster_file", + "unreachable_cluster_controller", + "no_cluster_controller", + "status_incomplete_client", + "status_incomplete_coordinators", + "status_incomplete_error", + "status_incomplete_timeout", + "status_incomplete_cluster", + "quorum_not_reachable" + ] + }, + "description":"The cluster file is not up to date." + } + ], + "timestamp":1415650089, + "cluster_file":{ + "path":"/etc/foundationdb/fdb.cluster", + "up_to_date":true + } + } +} diff --git a/documentation/sphinx/Makefile b/documentation/sphinx/Makefile index 505aa79bb7c..d23b136f1fe 100644 --- a/documentation/sphinx/Makefile +++ b/documentation/sphinx/Makefile @@ -68,7 +68,7 @@ buildsphinx: if [ ! -e $(SPHINXBUILD) ]; then \ mkdir $(BUILDDIR); \ cd $(BUILDDIR); \ - curl -O $(VENV_URL); \ + curl -OL $(VENV_URL); \ tar zxvf $(VENV_VERSION).tar.gz; \ ./$(VENV_VERSION)/virtualenv.py venv; \ fi diff --git a/documentation/sphinx/conf.py b/documentation/sphinx/conf.py index b0fad9da5b4..3afe72e7a02 100644 --- a/documentation/sphinx/conf.py +++ b/documentation/sphinx/conf.py @@ -31,7 +31,8 @@ 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'brokenrole', - 'relativelink' + 'relativelink', + 'sphinxcontrib.rubydomain' ] # Add any paths that contain templates here, relative to this directory. @@ -48,8 +49,7 @@ # General information about the project. project = u'FoundationDB' -copyright = u'2015 Apple, Inc. All Rights Reserved.' -author = u"Stephen Pimentel" +copyright = u'2013-2018 Apple, Inc and the FoundationDB project authors' # Load the version information from 'versions.target' import xml.etree.ElementTree as ET diff --git a/documentation/sphinx/extensions/brokenrole.py b/documentation/sphinx/extensions/brokenrole.py index eee78041523..71a0d120fc9 100644 --- a/documentation/sphinx/extensions/brokenrole.py +++ b/documentation/sphinx/extensions/brokenrole.py @@ -1,27 +1,27 @@ -# -# brokenrole.py -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# 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. -# - -def setup(app): - app.add_role('broken', broken_role) - -def broken_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - msg = inliner.reporter.error('Broken role invoked', line=lineno) - prb = inliner.problematic(rawtext,rawtext,msg) - return [prb],[msg] +# +# brokenrole.py +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors +# +# 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. +# + +def setup(app): + app.add_role('broken', broken_role) + +def broken_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + msg = inliner.reporter.error('Broken role invoked', line=lineno) + prb = inliner.problematic(rawtext,rawtext,msg) + return [prb],[msg] diff --git a/documentation/sphinx/extensions/relativelink.py b/documentation/sphinx/extensions/relativelink.py index a4a1707c512..4aea2e03163 100644 --- a/documentation/sphinx/extensions/relativelink.py +++ b/documentation/sphinx/extensions/relativelink.py @@ -1,48 +1,48 @@ -# -# relativelink.py -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# 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. -# - -from sphinx.addnodes import toctree - -# This extension cruelly monkey patches sphinx.environment.BuildEnvironment so -# that toctree entries can contain relative internal links, using the syntax -# Name -# This is translated into an href="relative/path" - -# Relative links already work fine outside the toctree: - -# Name _ - -def setup(app): - import sphinx.environment - from docutils import nodes - - old_resolve = sphinx.environment.BuildEnvironment.resolve_toctree - def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, - titles_only=False, collapse=False, includehidden=False): - result = old_resolve(self, docname, builder, toctree, prune=True, maxdepth=0, - titles_only=False, collapse=False, includehidden=False) - if result == None: - return result - - for node in result.traverse( nodes.reference ): - if not node['internal'] and node['refuri'].startswith("relative://"): - node['refuri'] = node['refuri'][len("relative://"):] - return result - sphinx.environment.BuildEnvironment.resolve_toctree = resolve_toctree +# +# relativelink.py +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors +# +# 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. +# + +from sphinx.addnodes import toctree + +# This extension cruelly monkey patches sphinx.environment.BuildEnvironment so +# that toctree entries can contain relative internal links, using the syntax +# Name +# This is translated into an href="relative/path" + +# Relative links already work fine outside the toctree: + +# Name _ + +def setup(app): + import sphinx.environment + from docutils import nodes + + old_resolve = sphinx.environment.BuildEnvironment.resolve_toctree + def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, + titles_only=False, collapse=False, includehidden=False): + result = old_resolve(self, docname, builder, toctree, prune=True, maxdepth=0, + titles_only=False, collapse=False, includehidden=False) + if result == None: + return result + + for node in result.traverse( nodes.reference ): + if not node['internal'] and node['refuri'].startswith("relative://"): + node['refuri'] = node['refuri'][len("relative://"):] + return result + sphinx.environment.BuildEnvironment.resolve_toctree = resolve_toctree diff --git a/documentation/sphinx/requirements.txt b/documentation/sphinx/requirements.txt index 0412c26be05..8cf8ae7f955 100644 --- a/documentation/sphinx/requirements.txt +++ b/documentation/sphinx/requirements.txt @@ -2,3 +2,4 @@ sphinx==1.5.6 sphinx-bootstrap-theme==0.4.8 pygments-style-solarized +sphinxcontrib-rubydomain==0.1dev-20100804 \ No newline at end of file diff --git a/documentation/sphinx/source/administration.rst b/documentation/sphinx/source/administration.rst index 83e8055b455..35ff083e0e5 100644 --- a/documentation/sphinx/source/administration.rst +++ b/documentation/sphinx/source/administration.rst @@ -10,6 +10,7 @@ Administration :titlesonly: moving-a-cluster + tls This document covers the administration of an existing FoundationDB cluster. We recommend you read this document before setting up a cluster for performance testing or production use. @@ -121,7 +122,7 @@ The cluster file contains a connection string consisting of a cluster identifier Together the ``description`` and the ``ID`` should uniquely identify a FoundationDB cluster. -A cluster file may contain comments, marked by the ``#`` character. All characters on a line after the first occurance of a ``#`` will be ignored. +A cluster file may contain comments, marked by the ``#`` character. All characters on a line after the first occurrence of a ``#`` will be ignored. Generally, a cluster file should not be modified manually. Incorrect modifications after a cluster is created could result in data loss. To change the set of coordination servers used by a cluster, see :ref:`configuration-choosing-coordination-servers`. To change the cluster ``description``, see :ref:`configuration-setting-cluster-description`. @@ -186,7 +187,7 @@ To temporarily or permanently remove one or more machines from a FoundationDB cl It is now safe to remove these machines or processes from the cluster. - ``exclude`` can be used to exclude either machines (by specifiying an IP address) or individual processes (by specificying an ``IP``:``PORT`` pair). + ``exclude`` can be used to exclude either machines (by specifying an IP address) or individual processes (by specifying an ``IP``:``PORT`` pair). .. note:: Addresses have the form ``IP``:``PORT``. This form is used even if TLS is enabled. @@ -228,46 +229,45 @@ Use the ``status`` command of ``fdbcli`` to determine if the cluster is up and r The database is available. Welcome to the fdbcli. For help, type `help'. -fdb> status - -Configuration: - Redundancy mode - triple - Storage engine - ssd-2 - Coordinators - 5 - Desired Proxies - 5 - Desired Logs - 8 - -Cluster: - FoundationDB processes - 272 - Machines - 16 - Memory availability - 14.5 GB per process on machine with least available - Retransmissions rate - 20 Hz - Fault Tolerance - 2 machines - Server time - 03/19/18 08:51:52 - -Data: - Replication health - Healthy - Moving data - 0.000 GB - Sum of key-value sizes - 3.298 TB - Disk space used - 15.243 TB - -Operating space: - Storage server - 1656.2 GB free on most full server - Log server - 1794.7 GB free on most full server - -Workload: - Read rate - 55990 Hz - Write rate - 14946 Hz - Transactions started - 6321 Hz - Transactions committed - 1132 Hz - Conflict rate - 0 Hz - -Backup and DR: - Running backups - 1 - Running DRs - 1 as primary - -Client time: 03/19/18 08:51:51 + fdb> status + + Configuration: + Redundancy mode - triple + Storage engine - ssd-2 + Coordinators - 5 + Desired Proxies - 5 + Desired Logs - 8 + + Cluster: + FoundationDB processes - 272 + Machines - 16 + Memory availability - 14.5 GB per process on machine with least available + Retransmissions rate - 20 Hz + Fault Tolerance - 2 machines + Server time - 03/19/18 08:51:52 + Data: + Replication health - Healthy + Moving data - 0.000 GB + Sum of key-value sizes - 3.298 TB + Disk space used - 15.243 TB + + Operating space: + Storage server - 1656.2 GB free on most full server + Log server - 1794.7 GB free on most full server + + Workload: + Read rate - 55990 Hz + Write rate - 14946 Hz + Transactions started - 6321 Hz + Transactions committed - 1132 Hz + Conflict rate - 0 Hz + + Backup and DR: + Running backups - 1 + Running DRs - 1 as primary + + Client time: 03/19/18 08:51:51 The summary fields are interpreted as follows: @@ -327,131 +327,132 @@ The ``status`` command can provide detailed statistics about the cluster and the fdb> status details -Configuration: - Redundancy mode - triple - Storage engine - ssd-2 - Coordinators - 5 - -Cluster: - FoundationDB processes - 85 - Machines - 5 - Memory availability - 7.4 GB per process on machine with least available - Retransmissions rate - 5 Hz - Fault Tolerance - 2 machines - Server time - 03/19/18 08:59:37 - -Data: - Replication health - Healthy - Moving data - 0.000 GB - Sum of key-value sizes - 87.068 GB - Disk space used - 327.819 GB - -Operating space: - Storage server - 888.2 GB free on most full server - Log server - 897.3 GB free on most full server - -Workload: - Read rate - 117 Hz - Write rate - 0 Hz - Transactions started - 43 Hz - Transactions committed - 1 Hz - Conflict rate - 0 Hz - -Process performance details: - 10.0.4.1:4500 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) - 10.0.4.1:4501 ( 1% cpu; 2% machine; 0.010 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4502 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4503 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4504 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4505 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4506 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4507 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4508 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4509 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4510 ( 1% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4511 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4512 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.1:4513 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.1:4514 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.1:4515 ( 12% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.1:4516 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.3 GB / 7.4 GB RAM ) - 10.0.4.2:4500 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) - 10.0.4.2:4501 ( 15% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4502 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4503 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4504 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4505 ( 18% cpu; 3% machine; 0.124 Gbps; 18% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4506 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4507 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4508 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4509 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4510 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4511 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4512 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.2:4513 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.2:4514 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.2:4515 ( 11% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.2:4516 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM ) - 10.0.4.3:4500 ( 14% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 3.0 GB / 7.4 GB RAM ) - 10.0.4.3:4501 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) - 10.0.4.3:4502 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) - 10.0.4.3:4503 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.3:4504 ( 7% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4505 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.3:4506 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4507 ( 2% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4508 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.3:4509 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4510 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.3:4511 ( 2% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4512 ( 2% cpu; 3% machine; 0.284 Gbps; 3% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.3:4513 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.3:4514 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) - 10.0.4.3:4515 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) - 10.0.4.3:4516 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) - 10.0.4.4:4500 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) - 10.0.4.4:4501 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4502 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4503 ( 2% cpu; 4% machine; 0.065 Gbps; 16% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4504 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.4:4505 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4506 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4507 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4508 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4509 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4510 ( 24% cpu; 4% machine; 0.065 Gbps; 15% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4511 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) - 10.0.4.4:4512 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.4:4513 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.4:4514 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.4:4515 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.4:4516 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.6 GB / 7.4 GB RAM ) - 10.0.4.5:4500 ( 6% cpu; 2% machine; 0.076 Gbps; 7% disk IO; 3.2 GB / 7.4 GB RAM ) - 10.0.4.5:4501 ( 2% cpu; 2% machine; 0.076 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4502 ( 1% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4503 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4504 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.5:4505 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.5:4506 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4507 ( 2% cpu; 2% machine; 0.076 Gbps; 6% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4508 ( 31% cpu; 2% machine; 0.076 Gbps; 8% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.5:4509 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4510 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) - 10.0.4.5:4511 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4512 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4513 ( 0% cpu; 2% machine; 0.076 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM ) - 10.0.4.5:4514 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.5:4515 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) - 10.0.4.5:4516 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM ) - -Coordination servers: - 10.0.4.1:4500 (reachable) - 10.0.4.2:4500 (reachable) - 10.0.4.3:4500 (reachable) - 10.0.4.4:4500 (reachable) - 10.0.4.5:4500 (reachable) - -Client time: 03/19/18 08:59:37 + Configuration: + Redundancy mode - triple + Storage engine - ssd-2 + Coordinators - 5 + + Cluster: + FoundationDB processes - 85 + Machines - 5 + Memory availability - 7.4 GB per process on machine with least available + Retransmissions rate - 5 Hz + Fault Tolerance - 2 machines + Server time - 03/19/18 08:59:37 + + Data: + Replication health - Healthy + Moving data - 0.000 GB + Sum of key-value sizes - 87.068 GB + Disk space used - 327.819 GB + + Operating space: + Storage server - 888.2 GB free on most full server + Log server - 897.3 GB free on most full server + + Workload: + Read rate - 117 Hz + Write rate - 0 Hz + Transactions started - 43 Hz + Transactions committed - 1 Hz + Conflict rate - 0 Hz + + Process performance details: + 10.0.4.1:4500 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) + 10.0.4.1:4501 ( 1% cpu; 2% machine; 0.010 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4502 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4503 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4504 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4505 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4506 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4507 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4508 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4509 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4510 ( 1% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4511 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4512 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.1:4513 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.1:4514 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.1:4515 ( 12% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.1:4516 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.3 GB / 7.4 GB RAM ) + 10.0.4.2:4500 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) + 10.0.4.2:4501 ( 15% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4502 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4503 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4504 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4505 ( 18% cpu; 3% machine; 0.124 Gbps; 18% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4506 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4507 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4508 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4509 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4510 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4511 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4512 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.2:4513 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.2:4514 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.2:4515 ( 11% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.2:4516 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM ) + 10.0.4.3:4500 ( 14% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 3.0 GB / 7.4 GB RAM ) + 10.0.4.3:4501 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) + 10.0.4.3:4502 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) + 10.0.4.3:4503 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.3:4504 ( 7% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4505 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.3:4506 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4507 ( 2% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4508 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.3:4509 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4510 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.3:4511 ( 2% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4512 ( 2% cpu; 3% machine; 0.284 Gbps; 3% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.3:4513 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.3:4514 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) + 10.0.4.3:4515 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) + 10.0.4.3:4516 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM ) + 10.0.4.4:4500 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM ) + 10.0.4.4:4501 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4502 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4503 ( 2% cpu; 4% machine; 0.065 Gbps; 16% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4504 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.4:4505 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4506 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4507 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4508 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4509 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4510 ( 24% cpu; 4% machine; 0.065 Gbps; 15% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4511 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM ) + 10.0.4.4:4512 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.4:4513 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.4:4514 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.4:4515 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.4:4516 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.6 GB / 7.4 GB RAM ) + 10.0.4.5:4500 ( 6% cpu; 2% machine; 0.076 Gbps; 7% disk IO; 3.2 GB / 7.4 GB RAM ) + 10.0.4.5:4501 ( 2% cpu; 2% machine; 0.076 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4502 ( 1% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4503 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4504 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.5:4505 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.5:4506 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4507 ( 2% cpu; 2% machine; 0.076 Gbps; 6% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4508 ( 31% cpu; 2% machine; 0.076 Gbps; 8% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.5:4509 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4510 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM ) + 10.0.4.5:4511 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4512 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4513 ( 0% cpu; 2% machine; 0.076 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM ) + 10.0.4.5:4514 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.5:4515 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM ) + 10.0.4.5:4516 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM ) + + Coordination servers: + 10.0.4.1:4500 (reachable) + 10.0.4.2:4500 (reachable) + 10.0.4.3:4500 (reachable) + 10.0.4.4:4500 (reachable) + 10.0.4.5:4500 (reachable) + + Client time: 03/19/18 08:59:37 + Several details about individual FoundationDB processes are displayed in a list format in parenthesis after the IP address and port: ======= ========================================================================= @@ -482,7 +483,7 @@ To make configuring, starting, stopping, and restarting ``fdbserver`` processes During normal operation, ``fdbmonitor`` is transparent, and you interact with it only by modifying the configuration in :ref:`foundationdb.conf ` and perhaps occasionally by :ref:`starting and stopping ` it manually. If some problem prevents an ``fdbserver`` or ``backup-agent`` process from starting or causes it to stop unexpectedly, ``fdbmonitor`` will log errors to the system log. -If kill_on_configuration_change parameter is unset or set to `true` in foundationdb.conf then fdbmonitor will restart on changes automatically. If this parameter is set to `false` it will not restart on changes. +If ``kill_on_configuration_change`` parameter is unset or set to ``true`` in foundationdb.conf then fdbmonitor will restart on changes automatically. If this parameter is set to ``false`` it will not restart on changes. .. _administration-managing-trace-files: @@ -633,59 +634,24 @@ You can now remove old client library versions from your clients. This is only t Version-specific notes on upgrading =================================== -Upgrading from 5.1.x +Upgrading from 5.2.x -------------------- -Upgrades from 5.0.x will keep all your old data and configuration settings. 5.1 has a new backup format so backups will need to be restarted after upgrading. - -Upgrading from 5.0.x --------------------- - -Upgrades from 5.0.x will keep all your old data and configuration settings. - -Upgrading from 4.6.x --------------------- - -Upgrades from 4.6.x will keep all your old data and configuration settings. - -Upgrading from 4.5.x --------------------- +Upgrades from 5.2.x will keep all your old data and configuration settings. -Upgrades from 4.5.x will keep all your old data and configuration settings. - -Upgrading from 4.4.x --------------------- - -Upgrades from 4.4.x will keep all your old data and configuration settings. - -Upgrading from 4.3.x --------------------- - -Backup and DR must be stopped before upgrading. Upgrades from 4.3.x will keep all your old data and configuration settings. - -Upgrading from 4.2.x --------------------- - -Backup and DR must be stopped before upgrading. Upgrades from 4.2.x will keep all your old data and configuration settings. - -Upgrading from 4.1.x --------------------- - -Backup and DR must be stopped before upgrading. Upgrades from 4.1.x will keep all your old data and configuration settings. - -Upgrading from 4.0.x --------------------- +Upgrading from 4.4.x - 5.1.x +---------------------------- -Backup and DR must be stopped before upgrading. Upgrades from 4.0.x will keep all your old data and configuration settings. +Upgrades from versions between 4.4.x and 5.1.x will keep all your old data and configuration settings. Backups that are running will automatically be aborted and must be restarted. -Upgrading from 3.x --------------------- +Upgrading from 3.0.x - 4.3.x +---------------------------- -To upgrade from versions prior to 4.0, you should first upgrade to 4.0 and then to the current version. +Backup and DR must be stopped before upgrading. Upgrades from versions between 3.0.x and 4.3.x will keep all your old data and configuration settings. .. _upgrading-from-older-versions: Upgrading from Older Versions ----------------------------- -Upgrades from versions older than 3.0.0 are no longer supported. To upgrade from an older version, first upgrade to 4.0.x, then upgrade to the desired version. +Upgrades from versions older than 3.0.0 are no longer supported. diff --git a/documentation/sphinx/source/api-c.rst b/documentation/sphinx/source/api-c.rst index 0fada7d4778..ba38b6b0052 100644 --- a/documentation/sphinx/source/api-c.rst +++ b/documentation/sphinx/source/api-c.rst @@ -118,7 +118,7 @@ API versioning Prior to including ``fdb_c.h``, you must define the :macro:`FDB_API_VERSION` macro. This, together with the :func:`fdb_select_api_version()` function, allows programs written against an older version of the API to compile and run with newer versions of the C library. The current version of the FoundationDB C API is |api-version|. :: - #define FDB_API_VERSION 510 + #define FDB_API_VERSION 520 #include .. function:: fdb_error_t fdb_select_api_version(int version) @@ -249,7 +249,7 @@ See :ref:`developer-guide-programming-with-futures` for further (language-indepe .. function:: fdb_error_t fdb_future_block_until_ready(FDBFuture* future) Blocks the calling thread until the given Future is ready. It will return success even if the Future is set to an error -- you must call :func:`fdb_future_get_error()` to determine that. :func:`fdb_future_block_until_ready()` will return an error only in exceptional conditions (e.g. out of memory or other operating system resources). - + .. warning:: Never call this function from a callback passed to :func:`fdb_future_set_callback()`. This may block the thread on which :func:`fdb_run_network()` was invoked, resulting in a deadlock. .. function:: fdb_bool_t fdb_future_is_ready(FDBFuture* future) @@ -324,19 +324,19 @@ See :ref:`developer-guide-programming-with-futures` for further (language-indepe Set to the length of the value (in bytes). |future-memory-mine| - + .. function:: fdb_error_t fdb_future_get_string_array(FDBFuture* future, const char*** out_strings, int* out_count) Extracts an array of null-terminated C strings from an :type:`FDBFuture` into caller-provided variables. |future-warning| - + |future-get-return1| |future-get-return2|. - + :data:`*out_strings` Set to point to the first string in the array. - + :data:`*out_count` Set to the number of strings in the array. - + |future-memory-mine| .. function:: fdb_error_t fdb_future_get_keyvalue_array(FDBFuture* future, FDBKeyValue const** out_kv, int* out_count, fdb_bool_t* out_more) @@ -512,15 +512,15 @@ Applications must provide error handling and an appropriate retry loop around th .. function:: FDBFuture* fdb_transaction_get_addresses_for_key(FDBTransaction* transaction, uint8_t const* key_name, int key_name_length) Returns a list of public network addresses as strings, one for each of the storage servers responsible for storing :data:`key_name` and its associated value. - + |future-return0| an array of strings. |future-return1| call :func:`fdb_future_get_string_array()` to extract the string array, |future-return2| - + :data:`key_name` A pointer to the name of the key whose location is to be queried. - + :data:`key_name_length` |length-of| :data:`key_name`. - + .. |range-limited-by| replace:: If this limit was reached before the end of the specified range, then the :data:`*more` return of :func:`fdb_future_get_keyvalue_array()` will be set to a non-zero value. .. function:: FDBFuture* fdb_transaction_get_range(FDBTransaction* transaction, uint8_t const* begin_key_name, int begin_key_name_length, fdb_bool_t begin_or_equal, int begin_offset, uint8_t const* end_key_name, int end_key_name_length, fdb_bool_t end_or_equal, int end_offset, int limit, int target_bytes, FDBStreamingMode mode, int iteration, fdb_bool_t snapshot, fdb_bool_t reverse) @@ -637,52 +637,52 @@ Applications must provide error handling and an appropriate retry loop around th .. function:: void fdb_transaction_atomic_op(FDBTransaction* transaction, uint8_t const* key_name, int key_name_length, uint8_t const* param, int param_length, FDBMutationType operationType) |sets-and-clears1| to perform the operation indicated by ``operationType`` with operand ``param`` to the value stored by the given key. - + |atomic-ops-blurb1| - + |atomic-ops-blurb2| - + |atomic-ops-blurb3| - + .. warning :: |atomic-ops-warning| - + |sets-and-clears2| - + :data:`key_name` A pointer to the name of the key whose value is to be mutated. - + :data:`key_name_length` |length-of| :data:`key_name`. - + :data:`param` A pointer to the parameter with which the atomic operation will mutate the value associated with :data:`key_name`. - + :data:`param_length` |length-of| :data:`param`. - + :data:`operation_type` One of the :type:`FDBMutationType` values indicating which operation should be performed. - + .. type:: FDBMutationType An enumeration of available opcodes to be passed to :func:`fdb_transaction_atomic_op()` - + :data:`FDB_MUTATION_TYPE_ADD` - + |atomic-add1| - + |atomic-add2| - + :data:`FDB_MUTATION_TYPE_AND` - + |atomic-and| - + :data:`FDB_MUTATION_TYPE_OR` - + |atomic-or| - + :data:`FDB_MUTATION_TYPE_XOR` - + |atomic-xor| :data:`FDB_MUTATION_TYPE_MAX` @@ -694,13 +694,13 @@ Applications must provide error handling and an appropriate retry loop around th :data:`FDB_MUTATION_TYPE_BYTE_MAX` |atomic-byte-max| - + :data:`FDB_MUTATION_TYPE_MIN` |atomic-min1| |atomic-max-min| - + :data:`FDB_MUTATION_TYPE_BYTE_MIN` |atomic-byte-min| @@ -726,7 +726,7 @@ Applications must provide error handling and an appropriate retry loop around th |atomic-versionstamps-2| .. warning :: |atomic-versionstamps-tuple-warning-value| - + .. function:: FDBFuture* fdb_transaction_commit(FDBTransaction* transaction) Attempts to commit the sets and clears previously applied to the database snapshot represented by :data:`transaction` to the actual database. The commit may or may not succeed -- in particular, if a conflicting transaction previously committed, then the commit must fail in order to preserve transactional isolation. If the commit does succeed, the transaction is durably committed to the database and all subsequently started transactions will observe its effects. @@ -799,11 +799,11 @@ Applications must provide error handling and an appropriate retry loop around th .. function:: void fdb_transaction_cancel(FDBTransaction* transaction) |transaction-cancel-blurb| - + .. warning :: |transaction-reset-cancel-warning| - + .. warning :: |transaction-commit-cancel-warning| - + .. _conflictRanges: .. function:: fdb_error_t fdb_transaction_add_conflict_range(FDBTransaction* transaction, uint8_t const* begin_key_name, int begin_key_name_length, uint8_t const* end_key_name, int end_key_name_length, FDBConflictRangeType type) diff --git a/documentation/sphinx/source/api-common.rst.inc b/documentation/sphinx/source/api-common.rst.inc index 4b9694c17e0..1feb6a8ea9d 100644 --- a/documentation/sphinx/source/api-common.rst.inc +++ b/documentation/sphinx/source/api-common.rst.inc @@ -1,530 +1,525 @@ -.. -*- mode: rst; -*- - -.. |separately-installed-bindings| replace:: - For the language binding to function, FoundationDB client binaries whose version is at least as recent - must be installed. If you upgrade a language binding to a new version, you may need to upgrade the FoundationDB client binaries as well. See :ref:`installing-client-binaries`. - -.. |project-dependency| replace:: - If you have a project with automatic dependency installation and have expressed a dependency on foundationdb, it may automatically install the lastest version of the language binding when you deploy your project to a new machine. If you have not also upgraded the Foundation client binary, an unplanned upgrade of the language binding may encounter an incompatibility. You should therefore configure any project dependency on foundationdb in coordination with your overall upgrade strategy. - -.. |client-installed-bindings| replace:: - The language binding requires FoundationDB client binaries whose version is at least as recent. The binding installed with FoundationDB installation will automatically satisfy this requirement. - -.. |api-version-rationale| replace:: - FoundationDB encapsulates multiple versions of its interface by requiring the client to explicitly specify the version of the API it uses. The purpose of this design is to allow you to upgrade the server, client libraries, or bindings without having to modify client code. The client libraries support all previous versions of the API. The API version specified by the client is used to control the behavior of the binding. You can therefore upgrade to more recent packages (and thus receive various improvements) without having to change your code. - -.. |api-version-multi-version-warning| replace:: - When using the :ref:`multi-version client API `, setting an API version that is not supported by a particular client library will prevent that client from being used to connect to the cluster. In particular, you should not advance the API version of your application after upgrading your client until the cluster has also been upgraded. - -.. |transaction-blurb1| replace:: - In FoundationDB, a transaction is a mutable snapshot of a database. All read and write operations on a transaction see and modify an otherwise-unchanging version of the database and only change the underlying database if and when the transaction is committed. Read operations do see the effects of previous write operations on the same transaction. Committing a transaction usually succeeds in the absence of :ref:`conflicts `. - -.. |transaction-blurb2| replace:: - Transactions group operations into a unit with the properties of *atomicity*, *isolation*, and *durability*. Transactions also provide the ability to maintain an application's invariants or integrity constraints, supporting the property of *consistency*. Together these properties are known as :ref:`ACID `. - -.. |transaction-blurb3| replace:: - Transactions are also causally consistent: once a transaction has been successfully committed, all subsequently created transactions will see the modifications made by it. - -.. |used-during-commit-blurb| replace:: - If any operation is performed on a transaction after a commit has been issued but before it has returned, both the commit and the operation will |error-raise-type| a :ref:`used_during_commit ` |error-type|. In this case, all subsequent operations on this transaction will |error-raise-type| this error until |reset-func-name| is called. - -.. |unknown-result-blurb| replace:: - As with other client/server databases, in some failure scenarios a client may be unable to determine whether a transaction succeeded. In these cases, |commit-func| will |error-raise-type| a :ref:`commit_unknown_result ` |error-type|. The |on-error-func| function treats this |error-type| as retryable, so retry loops that don't check for :ref:`commit_unknown_result ` could execute the transaction twice. In these cases, you must consider the idempotence of the transaction. - -.. |commit-unknown-result-blurb| replace:: - - |unknown-result-blurb| For more information, see :ref:`developer-guide-unknown-results`. - -.. |commit-outstanding-reads-blurb| replace:: - Normally, commit will wait for outstanding reads to return. However, if those reads were snapshot reads or the transaction option for disabling "read-your-writes" has been invoked, any outstanding reads will immediately return errors. - -.. |transaction-cancel-blurb| replace:: - Cancels the transaction. All pending or future uses of the transaction will |error-raise-type| a :ref:`transaction_cancelled ` |error-type|. The transaction can be used again after it is |reset-func-name|. - -.. |snapshot-blurb1| replace:: - Snapshot reads selectively relax FoundationDB's isolation property, reducing :ref:`conflicts ` but making it harder to reason about concurrency. - -.. |snapshot-blurb2| replace:: - By default, FoundationDB transactions guarantee :ref:`serializable isolation `, resulting in a state that is *as if* transactions were executed one at a time, even if they were executed concurrently. Serializability has little performance cost when there are few :ref:`conflicts ` but can be expensive when there are many. FoundationDB therefore also permits individual reads within a transaction to be done as snapshot reads. - -.. |snapshot-blurb3| replace:: - Snapshot reads differ from ordinary (serializable) reads by permitting the values they read to be modified by concurrent transactions, whereas serializable reads cause conflicts in that case. Like serializable reads, snapshot reads see the effects of prior writes in the same transaction. For more information on the use of snapshot reads, see :ref:`snapshot isolation`. - -.. |snapshot-blurb4| replace:: - Snapshot reads also interact with transaction commit a little differently than normal reads. If a snapshot read is outstanding when transaction commit is called that read will immediately return an error. (Normally, transaction commit will wait until outstanding reads return before committing.) - -.. |keys-values-blurb| replace:: - Keys and values in FoundationDB are simple byte strings. - -.. |keys-values-other-types-blurb| replace:: - To encode other data types, see :ref:`encoding-data-types` and the |tuple-layer|. - -.. |as-foundationdb-blurb| replace:: - In some cases, you may have objects that are used to *represent* specific keys or values (for example, see |subspace|). As a convenience, the language binding API can work seamlessly with such objects if they implement the |as-foundationdb-key| or |as-foundationdb-value| methods, respectively. API methods that accept a key will alternately accept an object that implements the |as-foundationdb-key| method. Likewise, API methods accepting a value will also accept an object that implements the |as-foundationdb-value| method. - -.. |as-foundationdb-warning| replace:: - |as-foundationdb-key| and |as-foundationdb-value| are not intended to implement serialization protocols for object storage. Use these functions only when your object represents a specific key or value. - -.. |database-blurb1| replace:: - |database-type| represents a FoundationDB database --- a mutable, lexicographically ordered mapping from binary keys to binary values. - -.. |database-blurb2| replace:: - Although |database-type| provides convenience methods for reading and writing, modifications to a database are usually via transactions, which are usually created and committed automatically by |database-auto|. - -.. |database-sync| replace:: - The convenience methods provided by |database-type| have the same signature as the corresponding methods of ``Transaction``. However, most of the |database-type| methods are fully synchronous. (An exception is the methods for watches.) As a result, the |database-type| methods do not support the use of :ref:`implicit parallelism with futures `. - -.. |keysel-blurb1| replace:: - FoundationDB's lexicographically ordered data model permits finding keys based on their order (for example, finding the first key in the database greater than a given key). Key selectors represent a description of a key in the database that could be resolved to an actual key by |get-key-func| or used directly as the beginning or end of a range in |get-range-func|. - -.. |keysel-blurb2| replace:: - For more about how key selectors work, see :ref:`key selectors`. - -.. |database-atomic-ops-idempotency-note| replace:: - - Note that since some atomic operations are not idempotent, the implicit use of |database-auto| could interact with a :ref:`commit_unknown_result ` |error-type| in unpredictable ways. For more information, see :ref:`developer-guide-unknown-results`. - -.. |atomic-ops-blurb1| replace:: - An atomic operation is a single database command that carries out several logical steps: reading the value of a key, performing a transformation on that value, and writing the result. Different atomic operations perform different transformations. Like other database operations, an atomic operation is used within a transaction; however, its use within a transaction will not cause the transaction to conflict. - -.. |atomic-ops-blurb2| replace:: - Atomic operations do not expose the current value of the key to the client but simply send the database the transformation to apply. In regard to conflict checking, an atomic operation is equivalent to a write without a read. It can only cause *other* transactions performing reads of the key to conflict. - -.. |atomic-ops-blurb3| replace:: - By combining these logical steps into a single, read-free operation, FoundationDB can guarantee that the transaction will not conflict due to the operation. This makes atomic operations ideal for operating on keys that are frequently modified. A common example is the use of a key-value pair as a counter. - -.. |atomic-ops-warning| replace:: - If a transaction uses both an atomic operation and a serializable read on the same key, the benefits of using the atomic operation (for both conflict checking and performance) are lost. - -.. |atomic-add1| replace:: - Performs an addition of little-endian integers. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-add2| replace:: - The integers to be added must be stored in a little-endian representation. They can be signed in two's complement representation or unsigned. You can add to an integer at a known offset in the value by prepending the appropriate number of zero bytes to ``param`` and padding with zero bytes to match the length of the value. However, this offset technique requires that you know the addition will not cause the integer field within the value to overflow. - -.. |atomic-and| replace:: - Performs a bitwise "and" operation. If the existing value in the database is not present, then ``param`` is stored in the database. If the existing value in the database is shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-or| replace:: - Performs a bitwise "or" operation. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-xor| replace:: - Performs a bitwise "xor" operation. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-max1| replace:: - Sets the value in the database to the larger of the existing value and ``param``. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-max-min| replace:: - Both the existing value and ``param`` are treated as unsigned integers. (This differs from the behavior of atomic addition.) - -.. |atomic-min1| replace:: - Sets the value in the database to the smaller of the existing value and ``param``. If the existing value in the database is not present, then ``param`` is stored in the database. If the existing value in the database is shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. - -.. |atomic-byte-min| replace:: - Performs lexicographic comparison of byte strings. If the existing value in the database is not present, then ``param`` is stored. Otherwise the smaller of the two values is then stored in the database. - -.. |atomic-byte-max| replace:: - Performs lexicographic comparison of byte strings. If the existing value in the database is not present, then ``param`` is stored. Otherwise the larger of the two values is then stored in the database. - -.. |atomic-set-versionstamped-key-1| replace:: - Transforms ``key`` using a versionstamp for the transaction. This key must be at least 12 bytes long. The final 2 bytes will be interpreted as a 16-bit little-endian integer denoting an index into the key at which to perform the transformation, and then trimmed off the key. The 10 bytes in the key beginning at the index will be overwritten with the versionstamp. If the index plus 10 bytes points past the end of the key, the result will be an error. Sets the transformed key in the database to ``param``. - -.. |atomic-set-versionstamped-key-2| replace:: - This operation is not compatible with |read-your-writes-disable-option| and will generate an error if used with it. - -.. |atomic-set-versionstamped-value| replace:: - Transforms ``param`` using a versionstamp for the transaction. This parameter must be at least 10 bytes long, and the first 10 bytes will be overwritten with the versionstamp. Sets ``key`` in the database to the transformed parameter. - -.. |atomic-versionstamps-1| replace:: - A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database. The last 2 bytes are monotonic in the serialization order for transactions. - -.. |atomic-versionstamps-2| replace:: - A transaction is not permitted to read any transformed key or value previously set within that transaction, and an attempt to do so will result in an error. - -.. |atomic-versionstamps-tuple-warning-key| replace:: - At this time, versionstamped keys are not compatible with the Tuple layer except in Java and Python. Note that this implies versionstamped keys may not be used with the Subspace and Directory layers except in those languages. - -.. |atomic-versionstamps-tuple-warning-value| replace:: - At this time, versionstamped values are not compatible with the Tuple layer. - -.. |api-version| replace:: 510 - -.. |streaming-mode-blurb1| replace:: - When using |get-range-func| and similar interfaces, API clients can request large ranges of the database to iterate over. Making such a request doesn't necessarily mean that the client will consume all of the data in the range - sometimes the client doesn't know how far it intends to iterate in advance. FoundationDB tries to balance latency and bandwidth by requesting data for iteration in batches. - -.. |streaming-mode-blurb2| replace:: - Streaming modes permit the API client to customize this performance tradeoff by providing extra information about how the iterator will be used. - -.. |tuple-layer-blurb| replace:: - The FoundationDB API comes with a built-in layer for encoding tuples into keys usable by FoundationDB. The encoded key maintains the same sort order as the original tuple: sorted first by the first element, then by the second element, etc. This makes the tuple layer ideal for building a variety of higher-level data models. - -.. |tuple-layer-note| replace:: - For general guidance on tuple usage, see the discussion in the document on :ref:`Data Modeling `. - -.. |transaction-reset-blurb| replace:: - Rollback a transaction, completely resetting it to its initial state. This is logically equivalent to destroying the transaction and creating a new one. - -.. |transaction-reset-cancel-warning| replace:: - Be careful if you are using |reset-func| and |cancel-func| concurrently with the same transaction. Since they negate each other's effects, a race condition between these calls will leave the transaction in an unknown state. - -.. |transaction-commit-cancel-warning| replace:: - If your program attempts to cancel a transaction after |commit-func| has been called but before it returns, unpredictable behavior will result. While it is guaranteed that the transaction will eventually end up in a cancelled state, the commit may or may not occur. Moreover, even if the call to |commit-func| appears to |error-raise-type| a :ref:`transaction_cancelled ` |error-type|, the commit may have occurred or may occur in the future. This can make it more difficult to reason about the order in which transactions occur. - -.. |transaction-get-committed-version-blurb| replace:: - Gets the version number at which a successful commit modified the database. This must be called only after the successful (non-error) completion of a call to |commit-func| on this Transaction, or the behavior is undefined. Read-only transactions do not modify the database when committed and will have a committed version of -1. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction. - -.. |transaction-get-versionstamp-blurb| replace:: - Returns a future which will contain the versionstamp which was used by any versionstamp operations in this transaction. This function must be called before a call to |commit-func| on this Transaction. The future will be ready only after the successful completion of a call to |commit-func| on this Transaction. Read-only transactions do not modify the database when committed and will result in the future completing with an error. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction. - -.. |transaction-watch-blurb| replace:: - - A watch's behavior is relative to the transaction that created it. A watch will report a change in relation to the key's value as readable by that transaction. The initial value used for comparison is either that of the transaction's read version or the value as modified by the transaction itself prior to the creation of the watch. If the value changes and then changes back to its initial value, the watch might not report the change. - -.. |transaction-watch-committed-blurb| replace:: - - Until the transaction that created it has been committed, a watch will not report changes made by *other* transactions. In contrast, a watch will immediately report changes made by the transaction itself. Watches cannot be created if the transaction has set |read-your-writes-disable-option|, and an attempt to do so will |error-raise-type| an :ref:`watches_disabled ` |error-type|. - -.. |transaction-watch-error-blurb| replace:: - - If the transaction used to create a watch encounters an |error-type| during commit, then the watch will be set with that |error-type|. A transaction whose :ref:`commit result is unknown ` will set all of its watches with the :ref:`commit_unknown_result ` |error-type|. If an uncommitted transaction is reset or destroyed, then any watches it created will be set with the :ref:`transaction_cancelled ` |error-type|. - -.. |transaction-watch-limit-blurb| replace:: - - By default, each database connection can have no more than 10,000 watches that have not yet reported a change. When this number is exceeded, an attempt to create a watch will |error-raise-type| a :ref:`too_many_watches ` |error-type|. This limit can be changed using |max-watches-database-option|. Because a watch outlives the transaction that creates it, any watch that is no longer needed should be cancelled by calling |future-cancel| on its returned future. - -.. |conflict-range-note| replace:: - - Most applications will use the serializable isolation that transactions provide by default and will not need to manipulate conflict ranges. - -.. |conflict-range-blurb| replace:: - - The following make it possible to add :ref:`conflict ranges ` to a transaction. - -.. |add-read-conflict-range-blurb| replace:: - - Adds a range of keys to the transaction's read conflict ranges as if you had read the range. As a result, other transactions that write a key in this range could cause the transaction to fail with a conflict. - -.. |add-read-conflict-key-blurb| replace:: - - Adds a key to the transaction's read conflict ranges as if you had read the key. As a result, other transactions that concurrently write this key could cause the transaction to fail with a conflict. - -.. |add-write-conflict-range-blurb| replace:: - - Adds a range of keys to the transaction's write conflict ranges as if you had cleared the range. As a result, other transactions that concurrently read a key in this range could fail with a conflict. - -.. |add-write-conflict-key-blurb| replace:: - - Adds a key to the transaction's write conflict ranges as if you had written the key. As a result, other transactions that concurrently read this key could fail with a conflict. - -.. |network-options-blurb| replace:: - - A singleton providing options which affect the entire FoundationDB client. Note that network options can also be :ref:`set using environment variables`. - -.. |option-trace-enable-blurb| replace:: - Enables trace file generation on this FoundationDB client. Trace files will be generated in the specified output directory. If the directory is specified as |null-type|, then the output directory will be the current working directory. - -.. |option-trace-enable-warning| replace:: - The specified output directory must be unique to this client. In the present release, trace logging does not allow two clients to share a directory. - -.. |option-trace-max-logs-size-blurb| replace:: - Sets the maximum size in bytes for the sum of this FoundationDB client's trace output files in a single log directory. - -.. |option-trace-roll-size-blurb| replace:: - Sets the maximum size in bytes of a single trace output file for this FoundationDB client. - -.. |network-options-warning| replace:: - - It is an error to set these options after the first call to |open-func| or |init-func| anywhere in your application. - -.. |tls-options-burb| replace:: - - The following options are only used when connecting to a :doc:`TLS-enabled cluster `. - -.. |option-tls-plugin-blurb| replace:: - - Sets the :ref:`TLS plugin ` to load. This option, if used, must be set before any other TLS options. - -.. |option-tls-cert-path-blurb| replace:: - - Sets the path for the file from which the :ref:`certificate chain ` will be loaded. - -.. |option-tls-key-path-blurb| replace:: - - Sets the path for the file from which to load the :ref:`private key ` corresponding to your own certificate. - -.. |option-tls-verify-peers-blurb| replace:: - - Sets the :ref:`peer certificate field verification criteria `. - -.. |option-tls-cert-bytes| replace:: - - Sets the certificate chain. - -.. |option-tls-key-bytes| replace:: - - Set the private key corresponding to your own certificate. - -.. |option-disable-multi-version-client-api| replace:: - - Disables the :ref:`multi-version client API ` and instead uses the local client directly. Must be set before setting up the network. - -.. |option-callbacks-on-external-threads| replace:: - - If set, callbacks from :ref:`external client libraries ` can be called from threads created by the FoundationDB client library. Otherwise, callbacks will be called from either the thread used to add the callback or the network thread. Setting this option can improve performance when connected using an external client, but may not be safe to use in all environments. Must be set before setting up the network. WARNING: This feature is considered experimental at this time. - -.. |option-external-client-library| replace:: - - Adds an external client library for use by the :ref:`multi-version client API `. Must be set before setting up the network. - -.. |option-external-client-directory| replace:: - - Searches the specified path for dynamic libraries and adds them to the list of client libraries for use by the :ref:`multi-version client API `. Must be set before setting up the network. - -.. |database-options-blurb| replace:: - - Database options alter the behavior of FoundationDB databases. - -.. |option-location-cache-size-blurb| replace:: - Set the size of the client location cache. Raising this value can boost performance in very large databases where clients access data in a near-random pattern. This value must be an integer in the range [0, 2\ :sup:`31`-1]. Defaults to 100000. - -.. |option-max-watches-blurb| replace:: - - Set the maximum number of watches allowed to be outstanding on a database connection. Increasing this number could result in increased resource usage. Reducing this number will not cancel any outstanding watches. Defaults to 10000 and cannot be larger than 1000000. - -.. |option-machine-id-blurb| replace:: - - Specify the machine ID of a server to be preferentially used for database operations. ID must be a string of up to 16 hexadecimal digits that was used to configure :ref:`fdbserver processes `. Load balancing uses this option for location-awareness, attempting to send database operations first to servers on a specified machine, then a specified datacenter, then returning to its default algorithm. - -.. |option-datacenter-id-blurb| replace:: - - Specify the datacenter ID to be preferentially used for database operations. ID must be a string of up to 16 hexadecimal digits that was used to configure :ref:`fdbserver processes `. Load balancing uses this option for location-awareness, attempting to send database operations first to servers on a specified machine, then a specified datacenter, then returning to its default algorithm. - -.. |transaction-options-blurb| replace:: - - Transaction options alter the behavior of FoundationDB transactions. FoundationDB defaults to extremely safe transaction behavior, and we have worked hard to make the performance excellent with the default setting, so you should not often need to use transaction options. - -.. |option-snapshot-ryw-enable-blurb| replace:: - - If this option is set an equal or more times in this transaction than the disable option, snapshot reads *will* see the effects of prior writes in the same transaction. - -.. |option-snapshot-ryw-disable-blurb| replace:: - - If this option is set more times in this transction than the enable option, snapshot reads will *not* see the effects of prior writes in the same transaction. - -.. |option-priority-batch-blurb| replace:: - This transaction should be treated as low priority (other transactions should be processed first). Useful for doing potentially saturating batch work without interfering with the latency of other operations. - -.. |option-priority-system-immediate-blurb| replace:: - - This transaction should be treated as extremely high priority, taking priority over other transactions and bypassing controls on transaction queuing. - -.. |option-priority-system-immediate-warning| replace:: - - This is intended for the use of internal database functions and low-level tools; use by applications may result in severe database performance or availability problems. - -.. |option-causal-read-risky-blurb| replace:: - - This transaction does not require the strict causal consistency guarantee that FoundationDB provides by default. The read version of the transaction will be a committed version, and usually will be the latest committed, but it might be an older version in the event of a fault or network partition. - -.. |option-causal-write-risky-blurb| replace:: - - The application either knows that this transaction will be self-conflicting (at least one read overlaps at least one set or clear), or is willing to accept a small risk that the transaction could be committed a second time after its commit apparently succeeds. This option provides a small performance benefit. - -.. |option-read-your-writes-disable-blurb| replace:: - - When this option is invoked, a read performed by a transaction will not see any prior mutations that occured in that transaction, instead seeing the value which was in the database at the transaction's read version. This option may provide a small performance benefit for the client, but also disables a number of client-side optimizations which are beneficial for transactions which tend to read and write the same keys within a single transaction. - -.. |option-read-your-writes-disable-note| replace:: - - It is an error to set this option after performing any reads or writes on the transaction. - -.. |option-read-ahead-disable-blurb| replace:: - - Disables read-ahead caching for range reads. Under normal operation, a transaction will read extra rows from the database into cache if range reads are used to page through a series of data one row at a time (i.e. if a range read with a one row limit is followed by another one row range read starting immediately after the result of the first). - -.. |option-access-system-keys-blurb| replace:: - - Allows this transaction to read and modify system keys (those that start with the byte ``0xFF``). - -.. |option-access-system-keys-warning| replace:: - - Writing into system keys will likely break your database. Further, even for readers, the format of data in the system keys may change from version to version in FoundationDB. - -.. |option-read-system-keys-blurb| replace:: - - Allows this transaction to read system keys (those that start with the byte ``0xFF``). - -.. |option-read-system-keys-warning| replace:: - - The format of data in the system keys may change from version to version in FoundationDB. - -.. |option-durability-dev-null-is-web-scale-blurb| replace:: - - This option has no effect yet, but may make users migrating from MongoDB more comfortable. - -.. |option-set-retry-limit-blurb1| replace:: - - Set a maximum number of retries after which additional calls to |on-error-func| will throw the most recently seen error code. (By default, a transaction permits an unlimited number of retries.) Valid parameter values are [-1, INT_MAX]. If set to -1, the transaction returns to the default of unlimited retries. - -.. |option-set-retry-limit-blurb2| replace:: - - Like all transaction options, the retry limit must be reset after a call to |on-error-func|. This behavior allows the user to make the retry limit dynamic. - -.. |option-set-max-retry-delay-blurb| replace:: - - Set the maximum backoff delay incurred in the call to |on-error-func| if the error is retryable. - -.. |option-set-timeout-blurb1| replace:: - - Set a timeout duration in milliseconds after which the transaction automatically to be cancelled. The time is measured from transaction creation (or the most call to |reset-func-name|, if any). Valid parameter values are [0, INT_MAX]. If set to 0, all timeouts will be disabled. Once a transaction has timed out, all pending or future uses of the transaction will |error-raise-type| a :ref:`transaction_timed_out ` |error-type|. The transaction can be used again after it is |reset-func-name|. - -.. |option-set-timeout-blurb2| replace:: - - Timeouts employ transaction cancellation, so you should note the issues raised by |cancel-func| when using timeouts. - -.. |option-set-timeout-blurb3| replace:: - - Like all transaction options, a timeout must be reset after a call to |on-error-func|. This behavior allows the user to make the timeout dynamic. Note that resetting this option resets only the timeout *duration*, not the starting point from which the time is measured. - -.. |option-next-write-no-write-conflict-range-blurb| replace:: - - The next write performed on this transaction will not generate a write conflict range. As a result, other transactions which read the key(s) being modified by the next write will not necessarily conflict with this transaction. - -.. |option-next-write-no-write-conflict-range-note| replace:: - - Care needs to be taken when using this option on a transaction that is shared between multiple threads. When setting this option, write conflict ranges will be disabled on the next write operation, regardless of what thread it is on. - -.. |future-blurb1| replace:: - Many FoundationDB API functions return "future" objects. A brief overview of futures is included in the :doc:`class scheduling tutorial `. Most future objects behave just like a normal object, but block when you use them for the first time if the asynchronous function which returned the future has not yet completed its action. A future object is considered ready when either a value is available, or when an error has occurred. - -.. |future-cancel-blurb| replace:: - - Cancels |future-type-string| and its associated asynchronous operation. If called before the future is ready, attempts to access its value will |error-raise-type| an :ref:`operation_cancelled ` |error-type|. Cancelling a future which is already ready has no effect. Note that even if a future is not ready, its associated asynchronous operation may have succesfully completed and be unable to be cancelled. - -.. |fdb-open-blurb| replace:: - Initializes the FoundationDB API, connects to the cluster specified by the :ref:`cluster file `, and opens the database with the specified name. This function is often called without any parameters, using only the defaults. If no cluster file is passed, FoundationDB automatically :ref:`determines a cluster file ` with which to connect to a cluster. - -.. |fdb-transactional-unknown-result-note| replace:: - In some failure scenarios, it is possible that your transaction will be executed twice. See :ref:`developer-guide-unknown-results` for more information. - -.. |db-attribute-blurb| replace:: - The |database-class| that this transaction is interacting with. - -.. |database-get-key-caching-blurb| replace:: - The key is cached, providing a potential performance benefit. However, the - value of the key is also retrieved, using network bandwidth. - -.. |transaction-get-key-caching-blurb| replace:: - By default, the key is cached for the duration of the transaction, providing - a potential performance benefit. However, the value of the key is also retrieved, - using network bandwidth. Invoking |read-your-writes-disable-option| will avoid - both the caching and the increased network bandwidth. - -.. |network-cannot-be-restarted-blurb| replace:: - Once the network is stopped it cannot be restarted during the lifetime of the running program. - -.. |fdb-careful-with-callbacks-blurb| replace:: - There are a number of requirements and constraints to be aware of when using callbacks with FoundationDB. Please read :ref:`developer-guide-programming-with-futures`. - -.. |subspace-blurb1| replace:: - Subspaces provide a convenient way to use the |tuple-layer| to define namespaces for different categories of data. The namespace is specified by a prefix tuple which is prepended to all tuples packed by the subspace. When unpacking a key with the subspace, the prefix tuple will be removed from the result. - -.. |subspace-blurb2| replace:: - As a best practice, API clients should use at least one subspace for application data. - -.. |subspace-blurb3| replace:: - Creates a subspace with the specified prefix tuple. If the raw prefix byte string is specified, then it will be prepended to all packed keys. Likewise, the raw prefix will be removed from all unpacked keys. - -.. |subspace-key-blurb| replace:: - Returns the key encoding the prefix used for the subspace. This is equivalent to packing the empty tuple. - -.. |subspace-pack-blurb| replace:: - Returns the key encoding the specified tuple in the subspace. For example, if you have a subspace with prefix tuple ``('users')`` and you use it to pack the tuple ``('Smith')``, the result is the same as if you packed the tuple ``('users', 'Smith')`` with the |tuple-layer|. - -.. |subspace-unpack-blurb| replace:: - Returns the tuple encoded by the given key, with the subspace's prefix tuple and raw prefix removed. - -.. |subspace-range-blurb| replace:: - Returns a range representing all keys in the subspace that encode tuples strictly starting with the specifed tuple. - -.. |subspace-contains-blurb| replace:: - Returns true if ``key`` starts with |key-meth|, indicating that the subspace logically contains ``key``. - -.. |subspace-as-foundationdb-key-blurb| replace:: - Returns the key encoding the prefix used for the subspace, like |key-meth|. - -.. |subspace-subspace-blurb| replace:: - Returns a new subspace which is equivalent to this subspace with its prefix tuple extended by the specified tuple. - -.. |directory-blurb1| replace:: - The FoundationDB API provides :ref:`directories ` as a tool for managing related |subspace-api|. Directories are a recommended approach for administering applications. Each application should create or open at least one directory to manage its subspaces. - -.. |directory-blurb2| replace:: - Directories are identified by hierarchical paths analogous to the paths in a Unix-like file system. A path is represented as |dir-path-type| of strings. Each directory has an associated subspace used to store its content. The directory layer maps each path to a short prefix used for the corresponding subspace. In effect, directories provide a level of indirection for access to subspaces. - -.. |directory-blurb3| replace:: - Except where noted, directory methods interpret the provided path(s) relative to the path of the directory object. When opening a directory, a byte string ``layer`` option may be specified as a metadata identifier. - -.. |directory-layer-blurb| replace:: - Each instance defines a new root directory. The subspaces |node-subspace| and |content-subspace| control where the directory metadata and contents, respectively, are stored. The default root directory has a |node-subspace| with raw prefix ``\xFE`` and a |content-subspace| with no prefix. Specifying more restrictive values for |node-subspace| and |content-subspace| will allow using the directory layer alongside other content in a database. If |allow-manual-prefixes| is false, attempts to create a directory with a manual prefix under the directory layer will |error-raise-type| an |error-type|. The default root directory does not allow manual prefixes. - -.. |directory-create-or-open-blurb| replace:: - Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. If the directory does not exist, it is created (creating parent directories if necessary). - -.. |directory-create-or-open-return-blurb| replace:: - Returns the directory and its contents as a |directory-subspace|. - -.. |directory-open-blurb| replace:: - Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. The method will |error-raise-type| an |error-type| if the directory does not exist. - -.. |directory-create-blurb| replace:: - Creates a directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. Parent directories are created if necessary. The method will |error-raise-type| an |error-type| if the given directory already exists. - -.. |directory-move-blurb| replace:: - Moves the directory at ``old_path`` to ``new_path``. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory does not exist at ``old_path``, a directory already exists at ``new_path``, or the parent directory of ``new_path`` does not exist. - -.. |directory-move-return-blurb| replace:: - Returns the directory at its new location as a |directory-subspace|. - -.. |directory-remove-blurb| replace:: - Removes the directory at ``path``, its contents, and all subdirectories. The method will |error-raise-type| an |error-type| if the directory does not exist. - -.. |directory-remove-warning| replace:: - Clients that have already opened the directory might still insert data into its contents after removal. - -.. |directory-remove-if-exists-blurb| replace:: - Checks if the directory at ``path`` exists and, if so, removes the directory, its contents, and all subdirectories. Returns ``true`` if the directory existed and ``false`` otherwise. - -.. |directory-exists-blurb| replace:: - Returns ``true`` if the directory at ``path`` exists and ``false`` otherwise. - -.. |directory-get-layer-blurb| replace:: - Returns the layer specified when the directory was created. - -.. |directory-get-path-blurb| replace:: - Returns the path with which the directory was opened. - -.. |directory-subspace-blurb| replace:: - A directory subspace represents a specific directory and its contents. It stores the ``path`` with which it was opened and supports all |directory-layer| methods for operating on itself and its subdirectories. It also implements all |subspace| methods for working with the contents of that directory. - -.. |directory-move-to-blurb| replace:: - Moves this directory to ``new_path``, interpreting ``new_path`` absolutely. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory already exists at ``new_path`` or the parent directory of ``new_path`` does not exist. - -.. |locality-api-blurb| replace:: - The FoundationDB API comes with a set of functions for discovering the storage locations of keys within your cluster. This information can be useful for advanced users who wish to take into account the location of keys in the design of applications or processes. - -.. |locality-get-boundary-keys-db-or-tr| replace:: - The first parameter to this function may be either a |database-class| or a |transaction-class|. If it is passed a |transaction-class|, the transaction will not be committed, reset, or modified in any way, nor will its transaction options (such as retry limit) be applied within the function. However, if the database is unavailable prior to the function call, any timeout set on the transaction will still trigger. - -.. |locality-get-boundary-keys-blurb| replace:: - - Returns a |lazy-iterator-object| of keys ``k`` such that ``begin <= k < end`` and ``k`` is located at the start of a contiguous range stored on a single server. - -.. |locality-get-boundary-keys-warning-danger| replace:: - - This method is not transactional. It will return an answer no older than the Transaction or Database object it is passed, but the returned boundaries are an estimate and may not represent the exact boundary locations at any database version. - -.. |locality-get-addresses-for-key-blurb| replace:: - - Returns a list of public network addresses as strings, one for each of the storage servers responsible for storing ``key`` and its associated value. - +.. -*- mode: rst; -*- + +.. |separately-installed-bindings| replace:: + For the language binding to function, FoundationDB client binaries whose version is at least as recent + must be installed. If you upgrade a language binding to a new version, you may need to upgrade the FoundationDB client binaries as well. See :ref:`installing-client-binaries`. + +.. |project-dependency| replace:: + If you have a project with automatic dependency installation and have expressed a dependency on foundationdb, it may automatically install the lastest version of the language binding when you deploy your project to a new machine. If you have not also upgraded the Foundation client binary, an unplanned upgrade of the language binding may encounter an incompatibility. You should therefore configure any project dependency on foundationdb in coordination with your overall upgrade strategy. + +.. |client-installed-bindings| replace:: + The language binding requires FoundationDB client binaries whose version is at least as recent. The binding installed with FoundationDB installation will automatically satisfy this requirement. + +.. |api-version-rationale| replace:: + FoundationDB encapsulates multiple versions of its interface by requiring the client to explicitly specify the version of the API it uses. The purpose of this design is to allow you to upgrade the server, client libraries, or bindings without having to modify client code. The client libraries support all previous versions of the API. The API version specified by the client is used to control the behavior of the binding. You can therefore upgrade to more recent packages (and thus receive various improvements) without having to change your code. + +.. |api-version-multi-version-warning| replace:: + When using the :ref:`multi-version client API `, setting an API version that is not supported by a particular client library will prevent that client from being used to connect to the cluster. In particular, you should not advance the API version of your application after upgrading your client until the cluster has also been upgraded. + +.. |transaction-blurb1| replace:: + In FoundationDB, a transaction is a mutable snapshot of a database. All read and write operations on a transaction see and modify an otherwise-unchanging version of the database and only change the underlying database if and when the transaction is committed. Read operations do see the effects of previous write operations on the same transaction. Committing a transaction usually succeeds in the absence of :ref:`conflicts `. + +.. |transaction-blurb2| replace:: + Transactions group operations into a unit with the properties of *atomicity*, *isolation*, and *durability*. Transactions also provide the ability to maintain an application's invariants or integrity constraints, supporting the property of *consistency*. Together these properties are known as :ref:`ACID `. + +.. |transaction-blurb3| replace:: + Transactions are also causally consistent: once a transaction has been successfully committed, all subsequently created transactions will see the modifications made by it. + +.. |used-during-commit-blurb| replace:: + If any operation is performed on a transaction after a commit has been issued but before it has returned, both the commit and the operation will |error-raise-type| a :ref:`used_during_commit ` |error-type|. In this case, all subsequent operations on this transaction will |error-raise-type| this error until |reset-func-name| is called. + +.. |unknown-result-blurb| replace:: + As with other client/server databases, in some failure scenarios a client may be unable to determine whether a transaction succeeded. In these cases, |commit-func| will |error-raise-type| a :ref:`commit_unknown_result ` |error-type|. The |on-error-func| function treats this |error-type| as retryable, so retry loops that don't check for :ref:`commit_unknown_result ` could execute the transaction twice. In these cases, you must consider the idempotence of the transaction. + +.. |commit-unknown-result-blurb| replace:: + + |unknown-result-blurb| For more information, see :ref:`developer-guide-unknown-results`. + +.. |commit-outstanding-reads-blurb| replace:: + Normally, commit will wait for outstanding reads to return. However, if those reads were snapshot reads or the transaction option for disabling "read-your-writes" has been invoked, any outstanding reads will immediately return errors. + +.. |transaction-cancel-blurb| replace:: + Cancels the transaction. All pending or future uses of the transaction will |error-raise-type| a :ref:`transaction_cancelled ` |error-type|. The transaction can be used again after it is |reset-func-name|. + +.. |snapshot-blurb1| replace:: + Snapshot reads selectively relax FoundationDB's isolation property, reducing :ref:`conflicts ` but making it harder to reason about concurrency. + +.. |snapshot-blurb2| replace:: + By default, FoundationDB transactions guarantee :ref:`serializable isolation `, resulting in a state that is *as if* transactions were executed one at a time, even if they were executed concurrently. Serializability has little performance cost when there are few :ref:`conflicts ` but can be expensive when there are many. FoundationDB therefore also permits individual reads within a transaction to be done as snapshot reads. + +.. |snapshot-blurb3| replace:: + Snapshot reads differ from ordinary (serializable) reads by permitting the values they read to be modified by concurrent transactions, whereas serializable reads cause conflicts in that case. Like serializable reads, snapshot reads see the effects of prior writes in the same transaction. For more information on the use of snapshot reads, see :ref:`snapshot isolation`. + +.. |snapshot-blurb4| replace:: + Snapshot reads also interact with transaction commit a little differently than normal reads. If a snapshot read is outstanding when transaction commit is called that read will immediately return an error. (Normally, transaction commit will wait until outstanding reads return before committing.) + +.. |keys-values-blurb| replace:: + Keys and values in FoundationDB are simple byte strings. + +.. |keys-values-other-types-blurb| replace:: + To encode other data types, see :ref:`encoding-data-types` and the |tuple-layer|. + +.. |as-foundationdb-blurb| replace:: + In some cases, you may have objects that are used to *represent* specific keys or values (for example, see |subspace|). As a convenience, the language binding API can work seamlessly with such objects if they implement the |as-foundationdb-key| or |as-foundationdb-value| methods, respectively. API methods that accept a key will alternately accept an object that implements the |as-foundationdb-key| method. Likewise, API methods accepting a value will also accept an object that implements the |as-foundationdb-value| method. + +.. |as-foundationdb-warning| replace:: + |as-foundationdb-key| and |as-foundationdb-value| are not intended to implement serialization protocols for object storage. Use these functions only when your object represents a specific key or value. + +.. |database-blurb1| replace:: + |database-type| represents a FoundationDB database --- a mutable, lexicographically ordered mapping from binary keys to binary values. + +.. |database-blurb2| replace:: + Although |database-type| provides convenience methods for reading and writing, modifications to a database are usually via transactions, which are usually created and committed automatically by |database-auto|. + +.. |database-sync| replace:: + The convenience methods provided by |database-type| have the same signature as the corresponding methods of ``Transaction``. However, most of the |database-type| methods are fully synchronous. (An exception is the methods for watches.) As a result, the |database-type| methods do not support the use of :ref:`implicit parallelism with futures `. + +.. |keysel-blurb1| replace:: + FoundationDB's lexicographically ordered data model permits finding keys based on their order (for example, finding the first key in the database greater than a given key). Key selectors represent a description of a key in the database that could be resolved to an actual key by |get-key-func| or used directly as the beginning or end of a range in |get-range-func|. + +.. |keysel-blurb2| replace:: + For more about how key selectors work, see :ref:`key selectors`. + +.. |database-atomic-ops-idempotency-note| replace:: + + Note that since some atomic operations are not idempotent, the implicit use of |database-auto| could interact with a :ref:`commit_unknown_result ` |error-type| in unpredictable ways. For more information, see :ref:`developer-guide-unknown-results`. + +.. |atomic-ops-blurb1| replace:: + An atomic operation is a single database command that carries out several logical steps: reading the value of a key, performing a transformation on that value, and writing the result. Different atomic operations perform different transformations. Like other database operations, an atomic operation is used within a transaction; however, its use within a transaction will not cause the transaction to conflict. + +.. |atomic-ops-blurb2| replace:: + Atomic operations do not expose the current value of the key to the client but simply send the database the transformation to apply. In regard to conflict checking, an atomic operation is equivalent to a write without a read. It can only cause *other* transactions performing reads of the key to conflict. + +.. |atomic-ops-blurb3| replace:: + By combining these logical steps into a single, read-free operation, FoundationDB can guarantee that the transaction will not conflict due to the operation. This makes atomic operations ideal for operating on keys that are frequently modified. A common example is the use of a key-value pair as a counter. + +.. |atomic-ops-warning| replace:: + If a transaction uses both an atomic operation and a serializable read on the same key, the benefits of using the atomic operation (for both conflict checking and performance) are lost. + +.. |atomic-add1| replace:: + Performs an addition of little-endian integers. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-add2| replace:: + The integers to be added must be stored in a little-endian representation. They can be signed in two's complement representation or unsigned. You can add to an integer at a known offset in the value by prepending the appropriate number of zero bytes to ``param`` and padding with zero bytes to match the length of the value. However, this offset technique requires that you know the addition will not cause the integer field within the value to overflow. + +.. |atomic-and| replace:: + Performs a bitwise "and" operation. If the existing value in the database is not present, then ``param`` is stored in the database. If the existing value in the database is shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-or| replace:: + Performs a bitwise "or" operation. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-xor| replace:: + Performs a bitwise "xor" operation. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-max1| replace:: + Sets the value in the database to the larger of the existing value and ``param``. If the existing value in the database is not present or shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-max-min| replace:: + Both the existing value and ``param`` are treated as unsigned integers. (This differs from the behavior of atomic addition.) + +.. |atomic-min1| replace:: + Sets the value in the database to the smaller of the existing value and ``param``. If the existing value in the database is not present, then ``param`` is stored in the database. If the existing value in the database is shorter than ``param``, it is first extended to the length of ``param`` with zero bytes. If ``param`` is shorter than the existing value in the database, the existing value is truncated to match the length of ``param``. + +.. |atomic-byte-min| replace:: + Performs lexicographic comparison of byte strings. If the existing value in the database is not present, then ``param`` is stored. Otherwise the smaller of the two values is then stored in the database. + +.. |atomic-byte-max| replace:: + Performs lexicographic comparison of byte strings. If the existing value in the database is not present, then ``param`` is stored. Otherwise the larger of the two values is then stored in the database. + +.. |atomic-set-versionstamped-key-1| replace:: + Transforms ``key`` using a versionstamp for the transaction. This key must be at least 12 bytes long. The final 2 bytes will be interpreted as a 16-bit little-endian integer denoting an index into the key at which to perform the transformation, and then trimmed off the key. The 10 bytes in the key beginning at the index will be overwritten with the versionstamp. If the index plus 10 bytes points past the end of the key, the result will be an error. Sets the transformed key in the database to ``param``. + +.. |atomic-set-versionstamped-key-2| replace:: + This operation is not compatible with |read-your-writes-disable-option| and will generate an error if used with it. + +.. |atomic-set-versionstamped-value| replace:: + Transforms ``param`` using a versionstamp for the transaction. This parameter must be at least 10 bytes long, and the first 10 bytes will be overwritten with the versionstamp. Sets ``key`` in the database to the transformed parameter. + +.. |atomic-versionstamps-1| replace:: + A versionstamp is a 10 byte, unique, monotonically (but not sequentially) increasing value for each committed transaction. The first 8 bytes are the committed version of the database. The last 2 bytes are monotonic in the serialization order for transactions. + +.. |atomic-versionstamps-2| replace:: + A transaction is not permitted to read any transformed key or value previously set within that transaction, and an attempt to do so will result in an error. + +.. |atomic-versionstamps-tuple-warning-key| replace:: + At this time, versionstamped keys are not compatible with the Tuple layer except in Java and Python. Note that this implies versionstamped keys may not be used with the Subspace and Directory layers except in those languages. + +.. |atomic-versionstamps-tuple-warning-value| replace:: + At this time, versionstamped values are not compatible with the Tuple layer. + +.. |api-version| replace:: 520 + +.. |streaming-mode-blurb1| replace:: + When using |get-range-func| and similar interfaces, API clients can request large ranges of the database to iterate over. Making such a request doesn't necessarily mean that the client will consume all of the data in the range - sometimes the client doesn't know how far it intends to iterate in advance. FoundationDB tries to balance latency and bandwidth by requesting data for iteration in batches. + +.. |streaming-mode-blurb2| replace:: + Streaming modes permit the API client to customize this performance tradeoff by providing extra information about how the iterator will be used. + +.. |tuple-layer-blurb| replace:: + The FoundationDB API comes with a built-in layer for encoding tuples into keys usable by FoundationDB. The encoded key maintains the same sort order as the original tuple: sorted first by the first element, then by the second element, etc. This makes the tuple layer ideal for building a variety of higher-level data models. + +.. |tuple-layer-note| replace:: + For general guidance on tuple usage, see the discussion in the document on :ref:`Data Modeling `. + +.. |transaction-reset-blurb| replace:: + Rollback a transaction, completely resetting it to its initial state. This is logically equivalent to destroying the transaction and creating a new one. + +.. |transaction-reset-cancel-warning| replace:: + Be careful if you are using |reset-func| and |cancel-func| concurrently with the same transaction. Since they negate each other's effects, a race condition between these calls will leave the transaction in an unknown state. + +.. |transaction-commit-cancel-warning| replace:: + If your program attempts to cancel a transaction after |commit-func| has been called but before it returns, unpredictable behavior will result. While it is guaranteed that the transaction will eventually end up in a cancelled state, the commit may or may not occur. Moreover, even if the call to |commit-func| appears to |error-raise-type| a :ref:`transaction_cancelled ` |error-type|, the commit may have occurred or may occur in the future. This can make it more difficult to reason about the order in which transactions occur. + +.. |transaction-get-committed-version-blurb| replace:: + Gets the version number at which a successful commit modified the database. This must be called only after the successful (non-error) completion of a call to |commit-func| on this Transaction, or the behavior is undefined. Read-only transactions do not modify the database when committed and will have a committed version of -1. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction. + +.. |transaction-get-versionstamp-blurb| replace:: + Returns a future which will contain the versionstamp which was used by any versionstamp operations in this transaction. This function must be called before a call to |commit-func| on this Transaction. The future will be ready only after the successful completion of a call to |commit-func| on this Transaction. Read-only transactions do not modify the database when committed and will result in the future completing with an error. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction. + +.. |transaction-watch-blurb| replace:: + + A watch's behavior is relative to the transaction that created it. A watch will report a change in relation to the key's value as readable by that transaction. The initial value used for comparison is either that of the transaction's read version or the value as modified by the transaction itself prior to the creation of the watch. If the value changes and then changes back to its initial value, the watch might not report the change. + +.. |transaction-watch-committed-blurb| replace:: + + Until the transaction that created it has been committed, a watch will not report changes made by *other* transactions. In contrast, a watch will immediately report changes made by the transaction itself. Watches cannot be created if the transaction has set |read-your-writes-disable-option|, and an attempt to do so will |error-raise-type| an :ref:`watches_disabled ` |error-type|. + +.. |transaction-watch-error-blurb| replace:: + + If the transaction used to create a watch encounters an |error-type| during commit, then the watch will be set with that |error-type|. A transaction whose :ref:`commit result is unknown ` will set all of its watches with the :ref:`commit_unknown_result ` |error-type|. If an uncommitted transaction is reset or destroyed, then any watches it created will be set with the :ref:`transaction_cancelled ` |error-type|. + +.. |transaction-watch-limit-blurb| replace:: + + By default, each database connection can have no more than 10,000 watches that have not yet reported a change. When this number is exceeded, an attempt to create a watch will |error-raise-type| a :ref:`too_many_watches ` |error-type|. This limit can be changed using |max-watches-database-option|. Because a watch outlives the transaction that creates it, any watch that is no longer needed should be cancelled by calling |future-cancel| on its returned future. + +.. |conflict-range-note| replace:: + + Most applications will use the serializable isolation that transactions provide by default and will not need to manipulate conflict ranges. + +.. |conflict-range-blurb| replace:: + + The following make it possible to add :ref:`conflict ranges ` to a transaction. + +.. |add-read-conflict-range-blurb| replace:: + + Adds a range of keys to the transaction's read conflict ranges as if you had read the range. As a result, other transactions that write a key in this range could cause the transaction to fail with a conflict. + +.. |add-read-conflict-key-blurb| replace:: + + Adds a key to the transaction's read conflict ranges as if you had read the key. As a result, other transactions that concurrently write this key could cause the transaction to fail with a conflict. + +.. |add-write-conflict-range-blurb| replace:: + + Adds a range of keys to the transaction's write conflict ranges as if you had cleared the range. As a result, other transactions that concurrently read a key in this range could fail with a conflict. + +.. |add-write-conflict-key-blurb| replace:: + + Adds a key to the transaction's write conflict ranges as if you had written the key. As a result, other transactions that concurrently read this key could fail with a conflict. + +.. |network-options-blurb| replace:: + + A singleton providing options which affect the entire FoundationDB client. Note that network options can also be :ref:`set using environment variables`. + +.. |option-trace-enable-blurb| replace:: + Enables trace file generation on this FoundationDB client. Trace files will be generated in the specified output directory. If the directory is specified as |null-type|, then the output directory will be the current working directory. + +.. |option-trace-enable-warning| replace:: + The specified output directory must be unique to this client. In the present release, trace logging does not allow two clients to share a directory. + +.. |option-trace-max-logs-size-blurb| replace:: + Sets the maximum size in bytes for the sum of this FoundationDB client's trace output files in a single log directory. + +.. |option-trace-roll-size-blurb| replace:: + Sets the maximum size in bytes of a single trace output file for this FoundationDB client. + +.. |network-options-warning| replace:: + + It is an error to set these options after the first call to |open-func| or |init-func| anywhere in your application. + +.. |tls-options-burb| replace:: + + The following options are only used when connecting to a :doc:`TLS-enabled cluster `. + +.. |option-tls-plugin-blurb| replace:: + + Sets the :ref:`TLS plugin ` to load. This option, if used, must be set before any other TLS options. + +.. |option-tls-cert-path-blurb| replace:: + + Sets the path for the file from which the :ref:`certificate chain ` will be loaded. + +.. |option-tls-key-path-blurb| replace:: + + Sets the path for the file from which to load the :ref:`private key ` corresponding to your own certificate. + +.. |option-tls-verify-peers-blurb| replace:: + + Sets the :ref:`peer certificate field verification criteria `. + +.. |option-tls-cert-bytes| replace:: + + Sets the certificate chain. + +.. |option-tls-key-bytes| replace:: + + Set the private key corresponding to your own certificate. + +.. |option-disable-multi-version-client-api| replace:: + + Disables the :ref:`multi-version client API ` and instead uses the local client directly. Must be set before setting up the network. + +.. |option-callbacks-on-external-threads| replace:: + + If set, callbacks from :ref:`external client libraries ` can be called from threads created by the FoundationDB client library. Otherwise, callbacks will be called from either the thread used to add the callback or the network thread. Setting this option can improve performance when connected using an external client, but may not be safe to use in all environments. Must be set before setting up the network. WARNING: This feature is considered experimental at this time. + +.. |option-external-client-library| replace:: + + Adds an external client library for use by the :ref:`multi-version client API `. Must be set before setting up the network. + +.. |option-external-client-directory| replace:: + + Searches the specified path for dynamic libraries and adds them to the list of client libraries for use by the :ref:`multi-version client API `. Must be set before setting up the network. + +.. |database-options-blurb| replace:: + + Database options alter the behavior of FoundationDB databases. + +.. |option-location-cache-size-blurb| replace:: + Set the size of the client location cache. Raising this value can boost performance in very large databases where clients access data in a near-random pattern. This value must be an integer in the range [0, 2\ :sup:`31`-1]. Defaults to 100000. + +.. |option-max-watches-blurb| replace:: + + Set the maximum number of watches allowed to be outstanding on a database connection. Increasing this number could result in increased resource usage. Reducing this number will not cancel any outstanding watches. Defaults to 10000 and cannot be larger than 1000000. + +.. |option-machine-id-blurb| replace:: + + Specify the machine ID of a server to be preferentially used for database operations. ID must be a string of up to 16 hexadecimal digits that was used to configure :ref:`fdbserver processes `. Load balancing uses this option for location-awareness, attempting to send database operations first to servers on a specified machine, then a specified datacenter, then returning to its default algorithm. + +.. |option-datacenter-id-blurb| replace:: + + Specify the datacenter ID to be preferentially used for database operations. ID must be a string of up to 16 hexadecimal digits that was used to configure :ref:`fdbserver processes `. Load balancing uses this option for location-awareness, attempting to send database operations first to servers on a specified machine, then a specified datacenter, then returning to its default algorithm. + +.. |transaction-options-blurb| replace:: + + Transaction options alter the behavior of FoundationDB transactions. FoundationDB defaults to extremely safe transaction behavior, and we have worked hard to make the performance excellent with the default setting, so you should not often need to use transaction options. + +.. |option-snapshot-ryw-enable-blurb| replace:: + + If this option is set an equal or more times in this transaction than the disable option, snapshot reads *will* see the effects of prior writes in the same transaction. + +.. |option-snapshot-ryw-disable-blurb| replace:: + + If this option is set more times in this transaction than the enable option, snapshot reads will *not* see the effects of prior writes in the same transaction. + +.. |option-priority-batch-blurb| replace:: + This transaction should be treated as low priority (other transactions should be processed first). Useful for doing potentially saturating batch work without interfering with the latency of other operations. + +.. |option-priority-system-immediate-blurb| replace:: + + This transaction should be treated as extremely high priority, taking priority over other transactions and bypassing controls on transaction queuing. + +.. |option-priority-system-immediate-warning| replace:: + + This is intended for the use of internal database functions and low-level tools; use by applications may result in severe database performance or availability problems. + +.. |option-causal-read-risky-blurb| replace:: + + This transaction does not require the strict causal consistency guarantee that FoundationDB provides by default. The read version of the transaction will be a committed version, and usually will be the latest committed, but it might be an older version in the event of a fault or network partition. + +.. |option-causal-write-risky-blurb| replace:: + + The application either knows that this transaction will be self-conflicting (at least one read overlaps at least one set or clear), or is willing to accept a small risk that the transaction could be committed a second time after its commit apparently succeeds. This option provides a small performance benefit. + +.. |option-read-your-writes-disable-blurb| replace:: + + When this option is invoked, a read performed by a transaction will not see any prior mutations that occured in that transaction, instead seeing the value which was in the database at the transaction's read version. This option may provide a small performance benefit for the client, but also disables a number of client-side optimizations which are beneficial for transactions which tend to read and write the same keys within a single transaction. + +.. |option-read-your-writes-disable-note| replace:: + + It is an error to set this option after performing any reads or writes on the transaction. + +.. |option-read-ahead-disable-blurb| replace:: + + Disables read-ahead caching for range reads. Under normal operation, a transaction will read extra rows from the database into cache if range reads are used to page through a series of data one row at a time (i.e. if a range read with a one row limit is followed by another one row range read starting immediately after the result of the first). + +.. |option-access-system-keys-blurb| replace:: + + Allows this transaction to read and modify system keys (those that start with the byte ``0xFF``). + +.. |option-access-system-keys-warning| replace:: + + Writing into system keys will likely break your database. Further, even for readers, the format of data in the system keys may change from version to version in FoundationDB. + +.. |option-read-system-keys-blurb| replace:: + + Allows this transaction to read system keys (those that start with the byte ``0xFF``). + +.. |option-read-system-keys-warning| replace:: + + The format of data in the system keys may change from version to version in FoundationDB. + +.. |option-set-retry-limit-blurb1| replace:: + + Set a maximum number of retries after which additional calls to |on-error-func| will throw the most recently seen error code. (By default, a transaction permits an unlimited number of retries.) Valid parameter values are [-1, INT_MAX]. If set to -1, the transaction returns to the default of unlimited retries. + +.. |option-set-retry-limit-blurb2| replace:: + + Like all transaction options, the retry limit must be reset after a call to |on-error-func|. This behavior allows the user to make the retry limit dynamic. + +.. |option-set-max-retry-delay-blurb| replace:: + + Set the maximum backoff delay incurred in the call to |on-error-func| if the error is retryable. + +.. |option-set-timeout-blurb1| replace:: + + Set a timeout duration in milliseconds after which the transaction automatically to be cancelled. The time is measured from transaction creation (or the most call to |reset-func-name|, if any). Valid parameter values are [0, INT_MAX]. If set to 0, all timeouts will be disabled. Once a transaction has timed out, all pending or future uses of the transaction will |error-raise-type| a :ref:`transaction_timed_out ` |error-type|. The transaction can be used again after it is |reset-func-name|. + +.. |option-set-timeout-blurb2| replace:: + + Timeouts employ transaction cancellation, so you should note the issues raised by |cancel-func| when using timeouts. + +.. |option-set-timeout-blurb3| replace:: + + Like all transaction options, a timeout must be reset after a call to |on-error-func|. This behavior allows the user to make the timeout dynamic. Note that resetting this option resets only the timeout *duration*, not the starting point from which the time is measured. + +.. |option-next-write-no-write-conflict-range-blurb| replace:: + + The next write performed on this transaction will not generate a write conflict range. As a result, other transactions which read the key(s) being modified by the next write will not necessarily conflict with this transaction. + +.. |option-next-write-no-write-conflict-range-note| replace:: + + Care needs to be taken when using this option on a transaction that is shared between multiple threads. When setting this option, write conflict ranges will be disabled on the next write operation, regardless of what thread it is on. + +.. |future-blurb1| replace:: + Many FoundationDB API functions return "future" objects. A brief overview of futures is included in the :doc:`class scheduling tutorial `. Most future objects behave just like a normal object, but block when you use them for the first time if the asynchronous function which returned the future has not yet completed its action. A future object is considered ready when either a value is available, or when an error has occurred. + +.. |future-cancel-blurb| replace:: + + Cancels |future-type-string| and its associated asynchronous operation. If called before the future is ready, attempts to access its value will |error-raise-type| an :ref:`operation_cancelled ` |error-type|. Cancelling a future which is already ready has no effect. Note that even if a future is not ready, its associated asynchronous operation may have succesfully completed and be unable to be cancelled. + +.. |fdb-open-blurb| replace:: + Initializes the FoundationDB API, connects to the cluster specified by the :ref:`cluster file `, and opens the database with the specified name. This function is often called without any parameters, using only the defaults. If no cluster file is passed, FoundationDB automatically :ref:`determines a cluster file ` with which to connect to a cluster. + +.. |fdb-transactional-unknown-result-note| replace:: + In some failure scenarios, it is possible that your transaction will be executed twice. See :ref:`developer-guide-unknown-results` for more information. + +.. |db-attribute-blurb| replace:: + The |database-class| that this transaction is interacting with. + +.. |database-get-key-caching-blurb| replace:: + The key is cached, providing a potential performance benefit. However, the + value of the key is also retrieved, using network bandwidth. + +.. |transaction-get-key-caching-blurb| replace:: + By default, the key is cached for the duration of the transaction, providing + a potential performance benefit. However, the value of the key is also retrieved, + using network bandwidth. Invoking |read-your-writes-disable-option| will avoid + both the caching and the increased network bandwidth. + +.. |network-cannot-be-restarted-blurb| replace:: + Once the network is stopped it cannot be restarted during the lifetime of the running program. + +.. |fdb-careful-with-callbacks-blurb| replace:: + There are a number of requirements and constraints to be aware of when using callbacks with FoundationDB. Please read :ref:`developer-guide-programming-with-futures`. + +.. |subspace-blurb1| replace:: + Subspaces provide a convenient way to use the |tuple-layer| to define namespaces for different categories of data. The namespace is specified by a prefix tuple which is prepended to all tuples packed by the subspace. When unpacking a key with the subspace, the prefix tuple will be removed from the result. + +.. |subspace-blurb2| replace:: + As a best practice, API clients should use at least one subspace for application data. + +.. |subspace-blurb3| replace:: + Creates a subspace with the specified prefix tuple. If the raw prefix byte string is specified, then it will be prepended to all packed keys. Likewise, the raw prefix will be removed from all unpacked keys. + +.. |subspace-key-blurb| replace:: + Returns the key encoding the prefix used for the subspace. This is equivalent to packing the empty tuple. + +.. |subspace-pack-blurb| replace:: + Returns the key encoding the specified tuple in the subspace. For example, if you have a subspace with prefix tuple ``('users')`` and you use it to pack the tuple ``('Smith')``, the result is the same as if you packed the tuple ``('users', 'Smith')`` with the |tuple-layer|. + +.. |subspace-unpack-blurb| replace:: + Returns the tuple encoded by the given key, with the subspace's prefix tuple and raw prefix removed. + +.. |subspace-range-blurb| replace:: + Returns a range representing all keys in the subspace that encode tuples strictly starting with the specifed tuple. + +.. |subspace-contains-blurb| replace:: + Returns true if ``key`` starts with |key-meth|, indicating that the subspace logically contains ``key``. + +.. |subspace-as-foundationdb-key-blurb| replace:: + Returns the key encoding the prefix used for the subspace, like |key-meth|. + +.. |subspace-subspace-blurb| replace:: + Returns a new subspace which is equivalent to this subspace with its prefix tuple extended by the specified tuple. + +.. |directory-blurb1| replace:: + The FoundationDB API provides :ref:`directories ` as a tool for managing related |subspace-api|. Directories are a recommended approach for administering applications. Each application should create or open at least one directory to manage its subspaces. + +.. |directory-blurb2| replace:: + Directories are identified by hierarchical paths analogous to the paths in a Unix-like file system. A path is represented as |dir-path-type| of strings. Each directory has an associated subspace used to store its content. The directory layer maps each path to a short prefix used for the corresponding subspace. In effect, directories provide a level of indirection for access to subspaces. + +.. |directory-blurb3| replace:: + Except where noted, directory methods interpret the provided path(s) relative to the path of the directory object. When opening a directory, a byte string ``layer`` option may be specified as a metadata identifier. + +.. |directory-layer-blurb| replace:: + Each instance defines a new root directory. The subspaces |node-subspace| and |content-subspace| control where the directory metadata and contents, respectively, are stored. The default root directory has a |node-subspace| with raw prefix ``\xFE`` and a |content-subspace| with no prefix. Specifying more restrictive values for |node-subspace| and |content-subspace| will allow using the directory layer alongside other content in a database. If |allow-manual-prefixes| is false, attempts to create a directory with a manual prefix under the directory layer will |error-raise-type| an |error-type|. The default root directory does not allow manual prefixes. + +.. |directory-create-or-open-blurb| replace:: + Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. If the directory does not exist, it is created (creating parent directories if necessary). + +.. |directory-create-or-open-return-blurb| replace:: + Returns the directory and its contents as a |directory-subspace|. + +.. |directory-open-blurb| replace:: + Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. The method will |error-raise-type| an |error-type| if the directory does not exist. + +.. |directory-create-blurb| replace:: + Creates a directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. Parent directories are created if necessary. The method will |error-raise-type| an |error-type| if the given directory already exists. + +.. |directory-move-blurb| replace:: + Moves the directory at ``old_path`` to ``new_path``. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory does not exist at ``old_path``, a directory already exists at ``new_path``, or the parent directory of ``new_path`` does not exist. + +.. |directory-move-return-blurb| replace:: + Returns the directory at its new location as a |directory-subspace|. + +.. |directory-remove-blurb| replace:: + Removes the directory at ``path``, its contents, and all subdirectories. The method will |error-raise-type| an |error-type| if the directory does not exist. + +.. |directory-remove-warning| replace:: + Clients that have already opened the directory might still insert data into its contents after removal. + +.. |directory-remove-if-exists-blurb| replace:: + Checks if the directory at ``path`` exists and, if so, removes the directory, its contents, and all subdirectories. Returns ``true`` if the directory existed and ``false`` otherwise. + +.. |directory-exists-blurb| replace:: + Returns ``true`` if the directory at ``path`` exists and ``false`` otherwise. + +.. |directory-get-layer-blurb| replace:: + Returns the layer specified when the directory was created. + +.. |directory-get-path-blurb| replace:: + Returns the path with which the directory was opened. + +.. |directory-subspace-blurb| replace:: + A directory subspace represents a specific directory and its contents. It stores the ``path`` with which it was opened and supports all |directory-layer| methods for operating on itself and its subdirectories. It also implements all |subspace| methods for working with the contents of that directory. + +.. |directory-move-to-blurb| replace:: + Moves this directory to ``new_path``, interpreting ``new_path`` absolutely. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory already exists at ``new_path`` or the parent directory of ``new_path`` does not exist. + +.. |locality-api-blurb| replace:: + The FoundationDB API comes with a set of functions for discovering the storage locations of keys within your cluster. This information can be useful for advanced users who wish to take into account the location of keys in the design of applications or processes. + +.. |locality-get-boundary-keys-db-or-tr| replace:: + The first parameter to this function may be either a |database-class| or a |transaction-class|. If it is passed a |transaction-class|, the transaction will not be committed, reset, or modified in any way, nor will its transaction options (such as retry limit) be applied within the function. However, if the database is unavailable prior to the function call, any timeout set on the transaction will still trigger. + +.. |locality-get-boundary-keys-blurb| replace:: + + Returns a |lazy-iterator-object| of keys ``k`` such that ``begin <= k < end`` and ``k`` is located at the start of a contiguous range stored on a single server. + +.. |locality-get-boundary-keys-warning-danger| replace:: + + This method is not transactional. It will return an answer no older than the Transaction or Database object it is passed, but the returned boundaries are an estimate and may not represent the exact boundary locations at any database version. + +.. |locality-get-addresses-for-key-blurb| replace:: + + Returns a list of public network addresses as strings, one for each of the storage servers responsible for storing ``key`` and its associated value. diff --git a/documentation/sphinx/source/api-python.rst b/documentation/sphinx/source/api-python.rst index 5b0f42f882d..5e8ba124b45 100644 --- a/documentation/sphinx/source/api-python.rst +++ b/documentation/sphinx/source/api-python.rst @@ -1,1442 +1,1437 @@ -.. default-domain:: py -.. highlight:: python -.. module:: fdb - -.. Required substitutions for api-common.rst.inc - -.. |database-type| replace:: ``Database`` -.. |database-class| replace:: :class:`Database` -.. |database-auto| replace:: the :func:`@fdb.transactional ` decorator -.. |transaction-class| replace:: :class:`Transaction` -.. |get-key-func| replace:: :func:`Transaction.get_key` -.. |get-range-func| replace:: :func:`Transaction.get_range` -.. |commit-func| replace:: :func:`Transaction.commit` -.. |reset-func-name| replace:: :func:`reset ` -.. |reset-func| replace:: :func:`Transaction.reset` -.. |cancel-func| replace:: :func:`Transaction.cancel` -.. |init-func| replace:: :func:`fdb.init` -.. |open-func| replace:: :func:`fdb.open` -.. |on-error-func| replace:: :meth:`Transaction.on_error` -.. |null-type| replace:: ``None`` -.. |error-type| replace:: exception -.. |error-raise-type| replace:: raise -.. |future-cancel| replace:: :func:`Future.cancel` -.. |max-watches-database-option| replace:: :func:`Database.options.set_max_watches` -.. |future-type-string| replace:: a :ref:`future ` -.. |read-your-writes-disable-option| replace:: :func:`Transaction.options.set_read_your_writes_disable` -.. |lazy-iterator-object| replace:: generator -.. |key-meth| replace:: :meth:`Subspace.key` -.. |directory-subspace| replace:: :ref:`DirectorySubspace ` -.. |directory-layer| replace:: :class:`DirectoryLayer` -.. |subspace| replace:: :class:`Subspace` -.. |subspace-api| replace:: :ref:`subspaces ` -.. |as-foundationdb-key| replace:: :meth:`as_foundationdb_key` -.. |as-foundationdb-value| replace:: :meth:`as_foundationdb_value` -.. |tuple-layer| replace:: :ref:`tuple layer ` -.. |dir-path-type| replace:: a tuple -.. |node-subspace| replace:: ``node_subspace`` -.. |content-subspace| replace:: ``content_subspace`` -.. |allow-manual-prefixes| replace:: ``allow_manual_prefixes`` - -.. include:: api-common.rst.inc - -####################### -Python API -####################### - -.. |block| replace:: :ref:`block ` -.. |future-type| replace:: (:ref:`future `) -.. |future-object| replace:: :ref:`Future ` object -.. |infrequent| replace:: *Infrequently used*. -.. |slice-defaults| replace:: The default slice begin is ``''``; the default slice end is ``'\xFF'``. -.. |byte-string| replace:: In Python 2, a byte string is a string of type ``str``. In Python 3, a byte string has type ``bytes``. - -Installation -============ - -The FoundationDB Python API is compatible with Python 2.7 - 3.4. You will need to have a Python version within this range on your system before the FoundationDB Python API can be installed. - -On macOS, the FoundationDB Python API is installed as part of the FoundationDB installation (see :ref:`installing-client-binaries`). On Ubuntu or RHEL/CentOS, you will need to install the FoundationDB Python API manually. - -You can download the FoundationDB Python API source directly from :doc:`downloads`. - -.. note:: The Python language binding is compatible with FoundationDB client binaries of version 2.0 or higher. When used with version 2.0.x client binaries, the API version must be set to 200 or lower. - -After installation, the module ``fdb`` should be usable from your Python installation or path. (The system default python is always used by the client installer on macOS.) - -.. _api-python-versioning: - -API versioning -============== - -When you import the ``fdb`` module, it exposes only one useful symbol: - -.. function:: api_version(version) - - Specifies the version of the API that the application uses. This allows future versions of FoundationDB to make API changes without breaking existing programs. The current version of the API is |api-version|. - -.. note:: You must call ``fdb.api_version(...)`` before using any other part of the API. Once you have done so, the rest of the API will become available in the ``fdb`` module. This requirement includes use of the ``@fdb.transactional`` decorator, which is called when your module is imported. - -.. note:: |api-version-rationale| - -.. warning:: |api-version-multi-version-warning| - -For API changes between version 13 and |api-version| (for the purpose of porting older programs), see :doc:`release-notes`. - -Opening a database -================== - -After importing the ``fdb`` module and selecting an API version, you probably want to open a :class:`Database`. The simplest way of doing this is using :func:`open`:: - - import fdb - fdb.api_version(510) - db = fdb.open() - -.. function:: open( cluster_file=None, db_name="DB", event_model=None ) - - |fdb-open-blurb| - - .. param event_model:: Can be used to select alternate :ref:`api-python-event-models` - - .. note:: In this release, db_name must be "DB". - - .. note:: ``fdb.open()`` combines the effect of :func:`init`, :func:`create_cluster`, and :meth:`Cluster.open_database`. - -.. function:: init() - - Initializes the FoundationDB API, creating a thread for the FoundationDB client and initializing the client's networking engine. :func:`init()` can only be called once. If called subsequently or after :func:`open`, it will raise an ``client_invalid_operation`` error. - -.. function:: create_cluster( cluster_file=None ) - - Connects to the cluster specified by :ref:`cluster_file `, or by a :ref:`default cluster file ` if - ``cluster_file`` is None. :func:`init` must be called first. - - Returns a |future-type| :class:`Cluster` object. - -.. data:: options - - |network-options-blurb| - - .. note:: |network-options-warning| - - .. method :: fdb.options.set_trace_enable( output_directory=None ) - - |option-trace-enable-blurb| - - .. warning:: |option-trace-enable-warning| - - .. method :: fdb.options.set_trace_max_logs_size(bytes) - - |option-trace-max-logs-size-blurb| - - .. method :: fdb.options.set_trace_roll_size(bytes) - - |option-trace-roll-size-blurb| - - .. method :: fdb.options.set_disable_multi_version_client_api() - - |option-disable-multi-version-client-api| - - .. method :: fdb.options.set_callbacks_on_external_threads() - - |option-callbacks-on-external-threads| - - .. method :: fdb.options.set_external_client_library(path_to_lib) - - |option-external-client-library| - - .. method :: fdb.options.set_external_client_directory(path_to_lib_directory) - - |option-external-client-directory| - - .. note:: |tls-options-burb| - - .. method :: fdb.options.set_tls_plugin(plugin_path_or_name) - - |option-tls-plugin-blurb| - - .. method :: fdb.options.set_tls_cert_path(path_to_file) - - |option-tls-cert-path-blurb| - - .. method :: fdb.options.set_tls_key_path(path_to_file) - - |option-tls-key-path-blurb| - - .. method :: fdb.options.set_tls_verify_peers(criteria) - - |option-tls-verify-peers-blurb| - - .. method :: fdb.options.set_tls_cert_bytes(bytes) - - |option-tls-cert-bytes| - - .. method :: fdb.options.set_tls_key_bytes(bytes) - - |option-tls-key-bytes| - -Cluster objects -=============== - -.. class:: Cluster - -.. method:: Cluster.open_database(name="DB") - - Opens a database with the given name. - - Returns a |future-type| :class:`Database` object. - - .. note:: In this release, name **must** be "DB". - -.. _api-python-keys: - -Keys and values -=============== - -|keys-values-blurb| |byte-string| - -|keys-values-other-types-blurb| - -``as_foundationdb_key`` and ``as_foundationdb_value`` ------------------------------------------------------ - -|as-foundationdb-blurb| - -.. warning :: |as-foundationdb-warning| - -KeyValue objects -================ - -.. class :: KeyValue - - Represents a single key-value pair in the database. This is a simple value type; mutating it won't affect your :class:`Transaction` or :class:`Database`. - - KeyValue supports the Python iterator protocol so that you can unpack a key and value directly into two variables:: - - for key, value in tr[begin:end]: - pass - -Attributes ----------- - -.. attribute:: KeyValue.key -.. attribute:: KeyValue.value - -Key selectors -============= - -|keysel-blurb1| - -|keysel-blurb2| - -.. class :: KeySelector(key, or_equal, offset) - - Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods: - -.. classmethod:: KeySelector.last_less_than(key) - - Returns a key selector referencing the last (greatest) key in the database less than the specified key. - -.. classmethod:: KeySelector.last_less_or_equal(key) - - Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. - -.. classmethod:: KeySelector.first_greater_than(key) - - Returns a key selector referencing the first (least) key greater than the specified key. - -.. classmethod:: KeySelector.first_greater_or_equal(key) - - Returns a key selector referencing the first key greater than, or equal to, the specified key. - -``KeySelector + offset`` - Adding an integer ``offset`` to a ``KeySelector`` returns a key selector referencing a key ``offset`` keys after the original ``KeySelector``. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. - -``KeySelector - offset`` - Subtracting an integer ``offset`` from a ``KeySelector`` returns a key selector referencing a key ``offset`` keys before the original ``KeySelector``. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. - -Database objects -================ - -.. class:: Database - -A |database-blurb1| |database-blurb2| - -.. note:: |database-sync| - -.. method:: Database.create_transaction() - - Returns a new :class:`Transaction` object. Consider using the :func:`@fdb.transactional ` decorator to create transactions instead, since it will automatically provide you with appropriate retry behavior. - -.. |sync-read| replace:: This read is fully synchronous. -.. |sync-write| replace:: This change will be committed immediately, and is fully synchronous. - -.. method:: Database.get(key) - - Returns the value associated with the specified key in the database (or ``None`` if the key does not exist). |sync-read| - -``X = db[key]`` - Shorthand for ``X = db.get(key)``. - -.. method:: Database.get_key(key_selector) - - Returns the key referenced by the specified :class:`KeySelector`. |sync-read| - - |database-get-key-caching-blurb| - -.. method:: Database.get_range(begin, end[, limit, reverse, streaming_mode]) - - Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as a list of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. |sync-read| - - Each of ``begin`` and ``end`` may be a key or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. - - If ``limit`` is specified, then only the first ``limit`` keys (and their values) in the range will be returned. - - If ``reverse`` is True, then the last ``limit`` keys in the range will be returned in reverse order. - - If ``streaming_mode`` is specified, it must be a value from the :data:`StreamingMode` enumeration. It provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. - -``X = db[begin:end]`` - Shorthand for ``X = db.get_range(begin, end)``. |slice-defaults| - -``X = db[begin:end:-1]`` - Shorthand for ``X = db.get_range(begin, end, reverse=True)``. |slice-defaults| - -.. method:: Database.get_range_startswith(prefix[, limit, reverse, streaming_mode]) - - Returns all keys ``k`` such that ``k.startswith(prefix)``, and their associated values, as a list of :class:`KeyValue` objects. The ``limit``, ``reverse`` and ``streaming_mode`` parameters have the same meanings as in :meth:`Database.get_range()`. - -.. method:: Database.set(key, value) - - Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |sync-write| - -``db[key] = value`` - Shorthand for ``db.set(key, value)``. - -.. method:: Database.clear(key) - - Removes the specified key (and any associated value), if it exists. |sync-write| - -``del db[key]`` - Shorthand for ``db.clear(key)``. - -.. method:: Database.clear_range(begin, end) - - Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |sync-write| - -``del db[begin:end]`` - Shorthand for ``db.clear_range(begin, end)``. |slice-defaults| - -.. method:: Database.clear_range_startswith(prefix) - - Removes all keys ``k`` such that ``k.startswith(prefix)``, and their associated values. |sync-write| - -.. method:: Database.get_and_watch(key) - - Returns a tuple ``value, watch``, where ``value`` is the value associated with ``key`` or ``None`` if the key does not exist, and ``watch`` is a :class:`FutureVoid` that will become ready after ``value`` changes. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.set_and_watch(key, value) - - Sets ``key`` to ``value`` and returns a :class:`FutureVoid` that will become ready after a subsequent change to ``value``. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.clear_and_watch(key) - - Removes ``key`` (and any associated value) if it exists and returns a :class:`FutureVoid` that will become ready after the value is subsequently set. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.add(key, param) -.. method:: Database.bit_and(key, param) -.. method:: Database.bit_or(key, param) -.. method:: Database.bit_xor(key, param) - - These atomic operations behave exactly like the :ref:`associated operations ` on :class:`Transaction` objects except that the change will immediately be committed, and is fully synchronous. - - .. note :: |database-atomic-ops-idempotency-note| - -.. _api-python-database-options: - -Database options ----------------- - -|database-options-blurb| - -.. method:: Database.options.set_location_cache_size(size) - - |option-location-cache-size-blurb| - -.. method:: Database.options.set_max_watches(max_watches) - - |option-max-watches-blurb| - -.. method:: Database.options.set_machine_id(id) - - |option-machine-id-blurb| - -.. method:: Database.options.set_datacenter_id(id) - - |option-datacenter-id-blurb| - -.. _api-python-transactional-decorator: - -Transactional decoration -======================== - -.. decorator:: transactional() - - The ``@fdb.transactional`` decorator is a convenience designed to concisely wrap a function with logic to automatically create a transaction and retry until success. - - For example:: - - @fdb.transactional - def simple_function(tr, x, y): - tr['foo'] = x - tr['bar'] = y - - The ``@fdb.transactional`` decorator makes ``simple_function`` a transactional function. All functions using this decorator must have an argument **named** ``tr``. This specially named argument is passed a transaction that the function can use to do reads and writes. - - A caller of a transactionally decorated function can pass a :class:`Database` instead of a transaction for the ``tr`` parameter. Then a transaction will be created automatically, and automatically committed before returning to the caller. The decorator will retry calling the decorated function until the transaction successfully commits. - - If ``db`` is a :class:`Database`, a call like :: - - simple_function(db, 'a', 'b') - - is equivalent to something like :: - - tr = db.create_transaction() - while True: - try: - simple_function(tr, 'a', 'b') - tr.commit().wait() - break - except fdb.FDBError as e: - tr.on_error(e).wait() - - A caller may alternatively pass an actual transaction to the ``tr`` parameter. In this case, the transactional function will not attempt to commit the transaction or to retry errors, since that is the responsibility of the caller who owns the transaction. This design allows transactionally decorated functions to be composed freely into larger transactions. - - .. note :: |fdb-transactional-unknown-result-note| - -Transaction objects -=================== - -.. class:: Transaction - -A ``Transaction`` object represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a ``Transaction``. - -|transaction-blurb1| - -|transaction-blurb2| - -|transaction-blurb3| - -The most convenient way to use Transactions is using the :func:`@fdb.transactional ` decorator. - -Keys and values in FoundationDB are byte strings (``str`` in Python 2.x, ``bytes`` in 3.x). To encode other data types, see the :mod:`fdb.tuple` module and :ref:`encoding-data-types`. - -Attributes ----------- - -.. attribute:: Transaction.db - - |db-attribute-blurb| - -Reading data ------------- - -.. method:: Transaction.get(key) - - Returns a |future-type| :class:`Value` associated with the specified key in the database. - - To check whether the specified key was present in the database, call :meth:`Value.present()` on the return value. - -``X = tr[key]`` - Shorthand for ``X = tr.get(key)``. - -.. method:: Transaction.get_key(key_selector) - - Returns the |future-type| :class:`Key` referenced by the specified :class:`KeySelector`. - - |transaction-get-key-caching-blurb| - -.. method:: Transaction.get_range(begin, end[, limit, reverse, streaming_mode]) - - Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an iterator yielding :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. - - Like a |future-object|, the returned iterator issues asynchronous read operations. It fetches the data in one or more efficient batches (depending on the value of the ``streaming_mode`` parameter). However, the iterator will block if iteration reaches a value whose read has not yet completed. - - Each of ``begin`` and ``end`` may be a key or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. - - If ``limit`` is specified, then only the first ``limit`` keys (and their values) in the range will be returned. - - If ``reverse`` is True, then the last ``limit`` keys in the range will be returned in reverse order. - - If ``streaming_mode`` is specified, it must be a value from the :data:`StreamingMode` enumeration. It provides a hint to FoundationDB about how the returned container is likely to be used. The default is :data:`StreamingMode.iterator`. - -``X = tr[begin:end]`` - Shorthand for ``X = tr.get_range(begin, end)``. |slice-defaults| - -``X = tr[begin:end:-1]`` - Shorthand for ``X = tr.get_range(begin, end, reverse=True)``. |slice-defaults| - -.. method:: Transaction.get_range_startswith(prefix[, limit, reverse, streaming_mode]) - - Returns all keys ``k`` such that ``k.startswith(prefix)``, and their associated values, as a container of :class:`KeyValue` objects (see :meth:`Transaction.get_range()` for a description of the returned container). - - The ``limit``, ``reverse`` and ``streaming_mode`` parameters have the same meanings as in :meth:`Transaction.get_range()`. - -.. _api-python-snapshot-reads: - -Snapshot reads --------------- - -.. attribute:: Transaction.snapshot - - |snapshot-blurb1| - - |snapshot-blurb2| - - |snapshot-blurb3| - - |snapshot-blurb4| - -.. attribute:: Transaction.snapshot.db - - |db-attribute-blurb| - -.. method:: Transaction.snapshot.get(key) - - Like :meth:`Transaction.get`, but as a snapshot read. - -``X = tr.snapshot[key]`` - Shorthand for ``X = tr.snapshot.get(key)``. - -.. method:: Transaction.snapshot.get_key(key_selector) -> key - - Like :meth:`Transaction.get_key`, but as a snapshot read. - -.. method:: Transaction.snapshot.get_range(begin, end[, limit, reverse, streaming_mode]) - - Like :meth:`Transaction.get_range`, but as a snapshot read. - -``X = tr.snapshot[begin:end]`` - Shorthand for ``X = tr.snapshot.get_range(begin, end)``. |slice-defaults| - -``X = tr.snapshot[begin:end:-1]`` - Shorthand for ``X = tr.snapshot.get_range(begin, end, reverse=True)``. |slice-defaults| - -.. method:: Transaction.snapshot.get_range_startswith(prefix[, limit, reverse, streaming_mode]) - - Like :meth:`Transaction.get_range_startswith`, but as a snapshot read. - -.. method:: Transaction.snapshot.get_read_version() - - Identical to :meth:`Transaction.get_read_version` (since snapshot and serializable reads use the same read version). - - -Writing data ------------- - -.. |immediate-return| replace:: Returns immediately, having modified the snapshot represented by this :class:`Transaction`. - -.. method:: Transaction.set(key,value) - - Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |immediate-return| - -``tr[key] = value`` - Shorthand for ``tr.set(key,value)``. - -.. method:: Transaction.clear(key) - - Removes the specified key (and any associated value), if it exists. |immediate-return| - -``del tr[key]`` - Shorthand for ``tr.clear(key)``. - -.. method:: Transaction.clear_range(begin, end) - - Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |immediate-return| - - .. note :: Unlike in the case of :meth:`get_range`, ``begin`` and ``end`` must be keys (byte strings), not :class:`KeySelector`\ s. (Resolving arbitrary key selectors would prevent this method from returning immediately, introducing concurrency issues.) - -``del tr[begin:end]`` - Shorthand for ``tr.clear_range(begin,end)``. |slice-defaults| - -.. method:: Transaction.clear_range_startswith(prefix) - - Removes all the keys ``k`` such that ``k.startswith(prefix)``, and their associated values. |immediate-return| - -.. _api-python-transaction-atomic-operations: - -Atomic operations ------------------ - -|atomic-ops-blurb1| - -|atomic-ops-blurb2| - -|atomic-ops-blurb3| - -.. warning :: |atomic-ops-warning| - -In each of the methods below, ``param`` should be a string appropriately packed to represent the desired value. For example:: - - # wrong - tr.add('key', 1) - - # right - import struct - tr.add('key', struct.pack('` for explanation and examples. - -.. method :: Transaction.commit() - - Attempt to commit the changes made in the transaction to the database. Returns a :class:`FutureVoid` representing the asynchronous result of the commit. You **must** call the :meth:`Future.wait()` method on the returned :class:`FutureVoid`, which will raise an exception if the commit failed. - - |commit-unknown-result-blurb| - - |commit-outstanding-reads-blurb| - - .. note :: Consider using the :func:`@fdb.transactional ` decorator, which not only calls :meth:`Database.create_transaction` and :meth:`Transaction.commit()` for you but also implements the required error handling and retry logic for transactions. - - .. warning :: |used-during-commit-blurb| - -.. method :: Transaction.on_error(exception) - - Determine whether an exception raised by a :class:`Transaction` method is retryable. Returns a :class:`FutureVoid`. You **must** call the :meth:`Future.wait()` method on the :class:`FutureVoid`, which will return after a delay if the exception was retryable, or re-raise the exception if it was not. - - .. note :: Consider using the :func:`@fdb.transactional ` decorator, which calls this method for you. - -.. method :: Transaction.reset() - - |transaction-reset-blurb| - -.. _api-python-cancel: - -.. method :: Transaction.cancel() - - |transaction-cancel-blurb| - - .. warning :: |transaction-reset-cancel-warning| - - .. warning :: |transaction-commit-cancel-warning| - -.. _api-python-watches: - -Watches -------- - -.. method:: Transaction.watch(key) - - Creates a watch and returns a :class:`FutureVoid` that will become ready when the watch reports a change to the value of the specified key. - - |transaction-watch-blurb| - - |transaction-watch-committed-blurb| - - |transaction-watch-error-blurb| - - |transaction-watch-limit-blurb| - -.. _api-python-conflict-ranges: - -Conflict ranges ---------------- - -.. note:: |conflict-range-note| - -|conflict-range-blurb| - -.. method:: Transaction.add_read_conflict_range(begin, end) - - |add-read-conflict-range-blurb| - -.. method:: Transaction.add_read_conflict_key(key) - - |add-read-conflict-key-blurb| - -.. method:: Transaction.add_write_conflict_range(begin, end) - - |add-write-conflict-range-blurb| - -.. method:: Transaction.add_write_conflict_key(key) - - |add-write-conflict-key-blurb| - -Versions --------- - -Most applications should use the read version that FoundationDB determines automatically during the transaction's first read, and ignore all of these methods. - -.. method :: Transaction.set_read_version(version) - - |infrequent| Sets the database version that the transaction will read from the database. The database cannot guarantee causal consistency if this method is used (the transaction's reads will be causally consistent only if the provided read version has that property). - -.. method :: Transaction.get_read_version() - - |infrequent| Returns a :class:`FutureVersion` representing the transaction's |future-type| read version. You must call the :meth:`Future.wait()` method on the returned object to retrieve the version as an integer. - -.. method :: Transaction.get_committed_version() - - |infrequent| |transaction-get-committed-version-blurb| - -.. method :: Transaction.get_versionstamp() - - |infrequent| |transaction-get-versionstamp-blurb| - -.. _api-python-transaction-options: - -Transaction options -------------------- - -|transaction-options-blurb| - -.. _api-python-snapshot-ryw: - -.. method:: Transaction.options.set_snapshot_ryw_disable - - |option-snapshot-ryw-disable-blurb| - -.. method:: Transaction.options.set_snapshot_ryw_enable - - |option-snapshot-ryw-enable-blurb| - -.. method:: Transaction.options.set_priority_batch - - |option-priority-batch-blurb| - -.. method:: Transaction.options.set_priority_system_immediate - - |option-priority-system-immediate-blurb| - - .. warning:: |option-priority-system-immediate-warning| - -.. method:: Transaction.options.set_causal_read_risky - - |option-causal-read-risky-blurb| - -.. method:: Transaction.options.set_causal_write_risky - - |option-causal-write-risky-blurb| - -.. _api-python-no-write-conflict-range: - -.. method:: Transaction.options.set_next_write_no_write_conflict_range - - |option-next-write-no-write-conflict-range-blurb| - - .. note:: |option-next-write-no-write-conflict-range-note| - -.. method:: Transaction.options.set_read_your_writes_disable - - |option-read-your-writes-disable-blurb| - - .. note:: |option-read-your-writes-disable-note| - -.. method:: Transaction.options.set_read_ahead_disable - - |option-read-ahead-disable-blurb| - -.. method:: Transaction.options.set_access_system_keys - - |option-access-system-keys-blurb| - - .. warning:: |option-access-system-keys-warning| - -.. method:: Transaction.options.set_read_system_keys - - |option-read-system-keys-blurb| - - .. warning:: |option-read-system-keys-warning| - -.. method:: Transaction.options.set_retry_limit - - |option-set-retry-limit-blurb1| - - |option-set-retry-limit-blurb2| - -.. method:: Transaction.options.set_max_retry_delay - - |option-set-max-retry-delay-blurb| - -.. _api-python-timeout: - -.. method:: Transaction.options.set_timeout - - |option-set-timeout-blurb1| - - |option-set-timeout-blurb2| - - |option-set-timeout-blurb3| - -.. method:: Transaction.options.set_durability_dev_null_is_web_scale - - |option-durability-dev-null-is-web-scale-blurb| - -.. _api-python-future: - -Future objects -============== - -|future-blurb1| - -.. _api-python-block: - -When a future object "blocks", what actually happens is determined by the :ref:`event model `. A threaded program will block a thread, but a program using the gevent model will block a greenlet. - -All future objects are a subclass of the :class:`Future` type. - -.. class:: Future - -.. method:: Future.wait() - - Blocks until the object is ready, and returns the object value (or raises an exception if the asynchronous function failed). - -.. method:: Future.is_ready() - - Immediately returns true if the future object is ready, false otherwise. - -.. method:: Future.block_until_ready() - - Blocks until the future object is ready. - -.. method:: Future.on_ready(callback) - - Calls the specified callback function, passing itself as a single argument, when the future object is ready. If the future object is ready at the time :meth:`on_ready()` is called, the call may occur immediately in the current thread (although this behavior is not guaranteed). Otherwise, the call may be delayed and take place on the thread with which the client was initialized. Therefore, the callback is responsible for any needed thread synchronization (and/or for posting work to your application's event loop, thread pool, etc., as may be required by your application's architecture). - -.. warning:: |fdb-careful-with-callbacks-blurb| - -.. method:: Future.cancel() - - |future-cancel-blurb| - -.. staticmethod:: Future.wait_for_any(*futures) - - Does not return until at least one of the given future objects is ready. Returns the index in the parameter list of a ready future object. - -Asynchronous methods return one of the following subclasses of :class:`Future`: - -.. class:: Value - - Represents a future string object and responds to the same methods as string in Python. They may be passed to FoundationDB methods that expect a string. - -.. method:: Value.present() - - Returns ``False`` if the key used to request this value was not present in the database. For example:: - - @fdb.transactional - def foo(tr): - val = tr['foo'] - if val.present(): - print 'Got value: %s' % val - else: - print 'foo was not present' - -.. class:: Key - - Represents a future string object and responds to the same methods as string in Python. They may be passed to FoundationDB methods that expect a string. - -.. class:: FutureVersion - - Represents a future version (integer). You must call the :meth:`Future.wait()` method on this object to retrieve the version as an integer. - -.. class:: FutureStringArray - - Represents a future list of strings. You must call the :meth:`Future.wait()` method on this object to retrieve the list of strings. - -.. class:: FutureVoid - - Represents a future returned from asynchronous methods that logically have no return value. - - For a :class:`FutureVoid` object returned by :meth:`Transaction.commit()` or :meth:`Transaction.on_error()`, you must call the :meth:`Future.wait()` method, which will either raise an exception if an error occurred during the asynchronous call, or do nothing and return ``None``. - -.. _streaming-mode-python: - -Streaming modes -=============== - -.. data:: StreamingMode - -|streaming-mode-blurb1| - -|streaming-mode-blurb2| - -The following streaming modes are available: - -.. data:: StreamingMode.iterator - - *The default.* The client doesn't know how much of the range it is likely to used and wants different performance concerns to be balanced. - - Only a small portion of data is transferred to the client initially (in order to minimize costs if the client doesn't read the entire range), and as the caller iterates over more items in the range larger batches will be transferred in order to maximize throughput. - -.. data:: StreamingMode.want_all - - The client intends to consume the entire range and would like it all transferred as early as possible. - -.. data:: StreamingMode.small - - |infrequent| Transfer data in batches small enough to not be much more expensive than reading individual rows, to minimize cost if iteration stops early. - -.. data:: StreamingMode.medium - - |infrequent| Transfer data in batches sized in between small and large. - -.. data:: StreamingMode.large - - |infrequent| Transfer data in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the client stops iteration early, some disk and network bandwidth may be wasted. The batch size may still be too small to allow a single client to get high throughput from the database, so if that is what you need consider :data:`StreamingMode.serial`. - -.. data:: StreamingMode.serial - - Transfer data in batches large enough that an individual client can get reasonable read bandwidth from the database. If the client stops iteration early, considerable disk and network bandwidth may be wasted. - -.. data:: StreamingMode.exact - - |infrequent| The client has passed a specific row limit and wants that many rows delivered in a single batch. This is not particularly useful in Python because iterator functionality makes batches of data transparent, so use :data:`StreamingMode.want_all` instead. - - -.. _api-python-event-models: - -Event models -============ - -By default, the FoundationDB Python API assumes that the calling program uses threads (as provided by the ``threading`` module) for concurrency. This means that blocking operations will block the current Python thread. This behavior can be changed by specifying the optional ``event_model`` parameter to the :func:`open` or :func:`init` functions. - -The following event models are available: - -``event_model=None`` - The default. Blocking operations will block the current Python thread. This is also fine for programs without any form of concurrency. - -``event_model="gevent"`` - The calling program uses the `gevent `_ module for single-threaded concurrency. Blocking operations will block the current greenlet. - - The FoundationDB Python API has been tested with gevent versions 0.13.8 and 1.0rc2 and should work with all gevent 0.13 and 1.0 releases. - - .. note:: The ``gevent`` event model on Windows requires gevent 1.0 or newer. - -``event_model="debug"`` - The calling program is threaded, but needs to be interruptible (by Ctrl-C). Blocking operations will poll, effectively blocking the current thread but responding to keyboard interrupts. This model is inefficient, but can be very useful for debugging. - -.. _api-python-release-notes: - -Errors -====== - -Errors in the FoundationDB API are raised as exceptions of type :class:`FDBError`. These errors may be displayed for diagnostic purposes, but generally should be passed to :meth:`Transaction.on_error`. When using :func:`@fdb.transactional `, appropriate errors will be retried automatically. - -.. class:: FDBError - -.. attribute:: FDBError.code - - An integer associated with the error type. - -.. attribute:: FDBError.description - - A somewhat human-readable description of the error. - -.. warning:: You should use only :attr:`FDBError.code` for programmatic comparisons, as the description of the error may change at any time. Whenever possible, use the :meth:`Transaction.on_error()` method to handle :class:`FDBError` exceptions. - -.. _api-python-tuple-layer: - -Tuple layer -=========== - -.. module:: fdb.tuple - -|tuple-layer-blurb| - -.. note:: |tuple-layer-note| - -The tuple layer in the FoundationDB Python API supports tuples that contain elements of the following data types: - -+-----------------------+-------------------------------------------------------------------------------+ -| Type | Legal Values | -+=======================+===============================================================================+ -| ``None`` | Any ``value`` such that ``value == None`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Byte string | Any ``value`` such that ``isinstance(value, bytes)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Unicode string | Any ``value`` such that ``isinstance(value, unicode)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Integer | Python 2.7: Any ``value`` such that ``isinstance(value, (int,long))`` and | -| | ``-2**2040+1 <= value <= 2**2040-1``. Python 3.x: Any ``value`` such that | -| | ``isinstance(value, int)`` and ``-2**2040+1 <= value <= 2**2040-1``. | -+-----------------------+-------------------------------------------------------------------------------+ -| Floating point number | Any ``value`` such that ``isinstance(value, fdb.tuple.SingleFloat)`` or | -| (single-precision) | ``isinstance(value, ctypes.c_float)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Floating point number | Any ``value`` such that ``isinstance(value, (ctypes.c_double, float))`` | -| (double-precision) | | -+-----------------------+-------------------------------------------------------------------------------+ -| Boolean | Any ``value`` such that ``isinstance(value, Boolean)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| UUID | Any ``value`` such that ``isinstance(value, uuid.UUID)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Versionstamp | Any ``value`` such that ``isinstance(value, fdb.tuple.Versionstamp)`` | -+-----------------------+-------------------------------------------------------------------------------+ -| Tuple or List | Any ``value`` such that ``isinstance(value, (tuple, list))`` and each element | -| | within ``value`` is one of the supported types with a legal value. | -+-----------------------+-------------------------------------------------------------------------------+ - -If ``T`` is a Python tuple meeting these criteria, then:: - - fdb.tuple.compare(T, fdb.tuple.unpack(fdb.tuple.pack(T))) == 0 - -That is, any tuple meeting these criteria will have the same semantic value if serialized and deserialized. For -the most part, this also implies that ``T == fdb.tuple.unpack(fdb.tuple.pack(T))`` with the following caveats: - -- Any ``value`` of type ``ctypes.c_double`` is converted to the Python ``float`` type, but - ``value.value == fdb.tuple.unpack(fdb.tuple.pack((value,)))[0]`` will be true (as long as ``value`` is not NaN). -- Any ``value`` of type ``ctypes.c_float`` is converted into a ``fdb.tuple.SingleFloat`` instance, but - ``value.value == fdb.tuple.unpack(fdb.tuple.pack((value,)))[0].value`` will be true (as long as ``value.value`` is not NaN). -- Any ``value`` of type ``list`` or ``tuple`` is converted to a ``tuple`` type where the elements of the serialized and deserialized ``value`` - will be equal (subject to these caveats) to the elements of the original ``value``. - -``import fdb.tuple`` - Imports the FoundationDB tuple layer. - -.. method:: pack(tuple, prefix=b'') - - Returns a key (byte string) encoding the specified tuple. If ``prefix`` is set, it will prefix the serialized - bytes with the prefix string. This throws an error if any of the tuple's items are incomplete `Versionstamp` - instances. - -.. method:: pack_with_versionstamp(tuple, prefix=b'') - - Returns a key (byte string) encoding the specified tuple. This method will throw an error unless - exactly one of the items of the tuple is an incomplete :class:`Versionstamp` instance. (It will - recurse down nested tuples if there are any to find one.) If so, it will produce a byte string - that can be fed into :meth:`fdb.Transaction.set_versionstamped_key` and correctly fill in the - versionstamp information at commit time so that when the key is re-read and deserialized, the - only difference is that the `Versionstamp` instance is complete and has the transaction version - filled in. This throws an error if there are no incomplete `Versionstamp` instances in the tuple - or if there is more than one. - -.. method:: unpack(key) - - Returns the tuple encoded by the given key. - -.. method:: has_incomplete_versionstamp(tuple) - - Returns ``True`` if there is at least one element contained within the tuple that is a - :class`Versionstamp` instance that is incomplete. If there are multiple incomplete - :class:`Versionstamp` instances, this method will return ``True``, but trying to pack it into a - byte string will result in an error. - -.. method:: range(tuple) - - Returns a Python slice object representing all keys that encode tuples strictly starting with ``tuple`` (that is, all tuples of greater length than tuple of which tuple is a prefix). - - Can be used to directly index a Transaction object to retrieve a range. For example:: - - tr[ fdb.tuple.range(('A',2)) ] - - returns all key-value pairs in the database whose keys would unpack to tuples like ('A', 2, x), ('A', 2, x, y), etc. - -.. method:: compare(tuple1, tuple2) - - Compares two tuples in a way that respects the natural ordering of the elements within the tuples. It will - return -1 if ``tuple1`` would sort before ``tuple2`` when performing an element-wise comparison of the two - tuples, it will return 1 if ``tuple1`` would sort after ``tuple2``, and it will return 0 if the two - tuples are equivalent. If the function must compare two elements of different types while doing the comparison, - it will sort the elements based on their internal type codes, so comparisons are consistent if not necessarily - semantically meaningful. Strings are sorted on their byte representation when encoded into UTF-8 (which may - differ from the default sort when non-ASCII characters are included within the string), and UUIDs are sorted - based on their big-endian byte representation. Single-precision floating point numbers are sorted before all - double-precision floating point numbers, and for floating point numbers, -NaN is sorted before -Infinity which - is sorted before finite numbers which are sorted before Infinity which is sorted before NaN. Different represetations - of NaN are not treated as equal. - - Additionally, the tuple serialization contract is such that after they are serialized, the byte-string representations - of ``tuple1`` and ``tuple2`` will sort in a manner that is consistent with this function. In particular, this function - obeys the following contract:: - - fdb.tuple.compare(tuple1, tuple2) == -1 if fdb.tuple.pack(tuple1) < fdb.tuple.pack(tuple2) else \ - 0 if fdb.tuple.pack(tuple2) == fdb.tuple.pack(tuple2) else 1 - - As byte order is the comparator used within the database, this comparator can be used to determine the order - of keys within the database. - -.. class:: SingleFloat(value) - - Wrapper around a single-precision floating point value. When constructed, the ``value`` parameter should either be - an integral value, a ``float``, or a ``ctypes.c_float``. It will then properly store the value in its - :attr:`SingleFloat.value` field (which should not be mutated). If the float does not fit within a IEEE 754 floating point - integer, there may be a loss of precision. - -.. attribute:: SingleFloat.value - - The underlying value of the ``SingleFloat`` object. This will have type ``float``. - -.. method:: SingleFloat.__eq__(other) -.. method:: SingleFloat.__ne__(other) -.. method:: SingleFloat.__lt__(other) -.. method:: SingleFloat.__le__(other) -.. method:: SingleFloat.__gt__(other) -.. method:: SingleFloat.__ge__(other) - - Comparison functions for ``SingleFloat`` objects. This will sort according to the byte representation - of the object rather than using standard float comparison. In particular, this means that ``-0.0 != 0.0`` - and that the ``NaN`` values will sort in a way that is consistent with the :meth:`compare` method between - tuples rather than using standard floating-point comparison. - -.. class:: Versionstamp(tr_version=None, user_version=0) - - Used to represent values written by versionstamp operations within the tuple layer. This - wraps a single byte array of length 12 that can be used to represent some global order - of items within the database. These versions are composed of two separate components: - (1) the 10-byte ``tr_version`` and (2) the two-byte ``user_version``. The ``tr_version`` - is set by the database, and it is used to impose an order between different transactions. - This order is guaranteed to be monotonically increasing over time for a given database. - (In particular, it imposes an order that is consistent with a serialization order of - the database's transactions.) If the client elects to leave the ``tr_version`` as its - default value of ``None``, then the ``Versionstamp`` is considered "incomplete". This - will cause the first 10 bytes of the serialized ``Versionstamp`` to be filled in with - dummy bytes when serialized. When used with :meth:`fdb.Transaction.set_versionstamped_key`, - an incomplete version can be used to ensure that a key gets written with the - current transaction's version which can be useful for maintaining append-only data - structures within the database. If the ``tr_version`` is set to something that is - not ``None``, it should be set to a byte array of length 10. In this case, the - ``Versionstamp`` is considered "complete". This is the usual case when one - reads a serialized ``Versionstamp`` from the database. - - The ``user_version`` should be specified as an integer, but it must fit within a two-byte - unsigned integer. It is set by the client, and it is used to impose an order between items - serialized within a single transaction. If left unset, then final two bytes of the serialized - ``Versionstamp`` are filled in with a default (constant) value. - - Sample usage of this class might be something like this:: - - @fdb.transactional - def write_versionstamp(tr, prefix): - tr.set_versionstamped_key(fdb.tuple.pack_with_versionstamp((prefix, fdb.tuple.Versionstamp())), b'') - return tr.get_versionstamp() - - @fdb.transactional - def read_versionstamp(tr, prefix): - subspace = fdb.Subspace((prefix,)) - for k, _ in tr.get_range(subspace.range().start, subspace.range().stop, 1): - return subspace.unpack(k)[0] - return None - - db = fdb.open() - del db[fdb.tuple.range(('prefix',))] - tr_version = write_versionstamp(db, 'prefix').wait() - v = read_versionstamp(db, 'prefix') - assert v == fdb.tuple.Versionstamp(tr_version=tr_version) - - Here, we serialize an incomplete ``Versionstamp`` and then write it using the ``set_versionstamped_key`` - mutation so that it picks up the transaction's version information. Then when we read it - back, we get a complete ``Versionstamp`` with the committed transaction's version. - -.. attribute:: Versionstamp.tr_version - - The inter-transaction component of the ``Versionstamp`` class. It should be either ``None`` (to - indicate an incomplete ``Versionstamp`` that will set the version later) or to some 10 byte value - indicating the commit version and batch version of some transaction. - -.. attribute:: Versionstamp.user_version - - The intra-transaction component of the ``Versionstamp`` class. It should be some number that can - fit within two bytes (i.e., between 0 and 65,535 inclusive). It can be used to impose an order - between items that are committed together in the same transaction. If left unset, then - the versionstamp is assigned a (constant) default user version value. - -.. method:: Versionstamp.from_bytes(bytes) - - Static initializer for ``Versionstamp`` instances that takes a serialized ``Versionstamp`` and - creates an instance of the class. The ``bytes`` parameter should be a byte string of length 12. - This method will serialize the version as a "complete" ``Versionstamp`` unless the dummy bytes - are equal to the default transaction version assigned to incomplete ``Versionstamps``. - -.. method:: Versionstamp.is_complete() - - Returns whether this version has been given a (non-``None``) ``tr_version`` or not. - -.. method:: Versionstamp.completed(tr_version) - - If this ``Versionstamp`` is incomplete, this returns a copy of this instance except that the - ``tr_version`` is filled in with the passed parameter. If the ``Versionstamp`` is already - complete, it will raise an error. - -.. method:: Versionstamp.to_bytes() - - Produces a serialized byte string corresponding to this versionstamp. It will have length 12 and - will combine the ``tr_version`` and ``user_version`` to produce a byte string that - lexicographically sorts appropriately with other ``Versionstamp`` instances. If this instance is - incomplete, then the ``tr_version`` component gets filled in with dummy bytes that will cause it - to sort after every complete ``Verionstamp``'s serialized bytes. - -.. method:: Versionstamp.__eq__(other) -.. method:: Versionstamp.__ne__(other) -.. method:: Versionstamp.__lt__(other) -.. method:: Versionstamp.__le__(other) -.. method:: Versionstamp.__gt__(other) -.. method:: Versionstamp.__ge__(other) - - Comparison functions for ``Versionstamp`` objects. For two complete ``Versionstamps``, the - ordering is first lexicographically by ``tr_version`` and then by ``user_version``. - Incomplete ``Versionstamps`` are defined to sort after all complete ``Versionstamps`` (the idea - being that for a given transaction, if a ``Versionstamp`` has been created as the result of - some prior transaction's work, then the incomplete ``Versionstamp``, when assigned a version, - will be assigned a greater version than the existing one), and for two incomplete ``Versionstamps``, - the order is by ``user_version`` only. - -.. _api-python-subspaces: - -Subspaces -========= - -.. currentmodule:: fdb - -|subspace-blurb1| - -|subspace-blurb2| - -.. note:: For general guidance on subspace usage, see the discussion in the :ref:`Developer Guide `. - -.. class:: Subspace(prefixTuple=tuple(), rawPrefix="") - - |subspace-blurb3| - -.. method:: Subspace.key() - - |subspace-key-blurb| - -.. method:: Subspace.pack(tuple=tuple()) - - |subspace-pack-blurb| - -.. method:: Subspace.pack_with_versionstamp(tuple) - - Returns the key encoding the specified tuple in the subspace so that it may be used as the key in the - :meth:`fdb.Transaction.set_versionstampe_key` method. The passed tuple must contain exactly one incomplete - :class:`fdb.tuple.Versionstamp` instance or the method will raise an error. The behavior here is the same - as if one used the :meth:`fdb.tuple.pack_with_versionstamp` method to appropriately pack together - this subspace and the passed tuple. - -.. method:: Subspace.unpack(key) - - |subspace-unpack-blurb| - -.. method:: Subspace.range(tuple=tuple()) - - |subspace-range-blurb| - - The range will be returned as a Python slice object, and may be used with any FoundationDB methods that require a range:: - - r = subspace.range(('A', 2)) - rng_itr1 = tr[r] - rng_itr2 = tr.get_range(r.start, r.stop, limit=1) - -.. method:: Subspace.contains(key) - - |subspace-contains-blurb| - -.. method:: Subspace.as_foundationdb_key() - - |subspace-as-foundationdb-key-blurb| This method serves to support the :ref:`as_foundationdb_key() ` convenience interface. - -.. method:: Subspace.subspace(tuple) - - |subspace-subspace-blurb| - - ``x = subspace[item]`` - - Shorthand for ``x = subspace.subspace((item,))``. This function can be combined with the :meth:`Subspace.as_foundationdb_key()` convenience to turn this:: - - s = fdb.Subspace(('x',)) - tr[s.pack(('foo', 'bar', 1))] = '' - - into this:: - - s = fdb.Subspace(('x',)) - tr[s['foo']['bar'][1]] = '' - -.. _api-python-directories: - -Directories -=========== - -|directory-blurb1| - -.. note:: For general guidance on directory usage, see the discussion in the :ref:`Developer Guide `. - -|directory-blurb2| - -|directory-blurb3| - -.. data:: directory - - The default instance of :class:`DirectoryLayer`. - -.. class:: DirectoryLayer(node_subspace=Subspace(rawPrefix="\xfe"), content_subspace=Subspace(), allow_manual_prefixes=False) - - |directory-layer-blurb| - -.. method:: DirectoryLayer.create_or_open(tr, path, layer=None) - - |directory-create-or-open-blurb| - - If the byte string ``layer`` is specified and the directory is new, it is recorded as the layer; if ``layer`` is specified and the directory already exists, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.open(tr, path, layer=None) - - |directory-open-blurb| - - If the byte string ``layer`` is specified, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.create(tr, path, layer=None, prefix=None) - - |directory-create-blurb| - - If the byte string ``prefix`` is specified, the directory is created with the given physical prefix; otherwise a prefix is allocated automatically. - - If the byte string ``layer`` is specified, it is recorded with the directory and will be checked by future calls to open. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.move(tr, old_path, new_path) - - |directory-move-blurb| - - |directory-move-return-blurb| - -.. method:: DirectoryLayer.remove(tr, path) - - |directory-remove-blurb| - - .. warning:: |directory-remove-warning| - -.. method:: DirectoryLayer.remove_if_exists(tr, path) - - |directory-remove-if-exists-blurb| - - .. warning:: |directory-remove-warning| - -.. method:: DirectoryLayer.list(tr, path=()) - - Returns a list of names of the immediate subdirectories of the directory at ``path``. Each name is a unicode string representing the last component of a subdirectory's path. - -.. method:: DirectoryLayer.exists(tr, path) - - |directory-exists-blurb| - -.. method:: DirectoryLayer.get_layer() - - |directory-get-layer-blurb| - -.. method:: DirectoryLayer.get_path() - - |directory-get-path-blurb| - -.. _api-python-directory-subspace: - -DirectorySubspace ------------------ - -|directory-subspace-blurb| - -.. method:: DirectorySubspace.move_to(tr, new_path) - - |directory-move-to-blurb| - - |directory-move-return-blurb| - -.. _api-python-locality: - -Locality information -==================== - -.. module:: fdb.locality - -|locality-api-blurb| - -.. method:: fdb.locality.get_boundary_keys(db_or_tr, begin, end) - - |locality-get-boundary-keys-blurb| - - |locality-get-boundary-keys-db-or-tr| - - Like a |future-object|, the returned container issues asynchronous read operations to fetch the data in the range and may block while iterating over its values if the read has not completed. - - |locality-get-boundary-keys-warning-danger| - -.. method:: fdb.locality.get_addresses_for_key(tr, key) - - Returns a :class:`fdb.FutureStringArray`. You must call the :meth:`fdb.Future.wait()` method on this object to retrieve a list of public network addresses as strings, one for each of the storage servers responsible for storing ``key`` and its associated value. - +.. default-domain:: py +.. highlight:: python +.. module:: fdb + +.. Required substitutions for api-common.rst.inc + +.. |database-type| replace:: ``Database`` +.. |database-class| replace:: :class:`Database` +.. |database-auto| replace:: the :func:`@fdb.transactional ` decorator +.. |transaction-class| replace:: :class:`Transaction` +.. |get-key-func| replace:: :func:`Transaction.get_key` +.. |get-range-func| replace:: :func:`Transaction.get_range` +.. |commit-func| replace:: :func:`Transaction.commit` +.. |reset-func-name| replace:: :func:`reset ` +.. |reset-func| replace:: :func:`Transaction.reset` +.. |cancel-func| replace:: :func:`Transaction.cancel` +.. |init-func| replace:: :func:`fdb.init` +.. |open-func| replace:: :func:`fdb.open` +.. |on-error-func| replace:: :meth:`Transaction.on_error` +.. |null-type| replace:: ``None`` +.. |error-type| replace:: exception +.. |error-raise-type| replace:: raise +.. |future-cancel| replace:: :func:`Future.cancel` +.. |max-watches-database-option| replace:: :func:`Database.options.set_max_watches` +.. |future-type-string| replace:: a :ref:`future ` +.. |read-your-writes-disable-option| replace:: :func:`Transaction.options.set_read_your_writes_disable` +.. |lazy-iterator-object| replace:: generator +.. |key-meth| replace:: :meth:`Subspace.key` +.. |directory-subspace| replace:: :ref:`DirectorySubspace ` +.. |directory-layer| replace:: :class:`DirectoryLayer` +.. |subspace| replace:: :class:`Subspace` +.. |subspace-api| replace:: :ref:`subspaces ` +.. |as-foundationdb-key| replace:: :meth:`as_foundationdb_key` +.. |as-foundationdb-value| replace:: :meth:`as_foundationdb_value` +.. |tuple-layer| replace:: :ref:`tuple layer ` +.. |dir-path-type| replace:: a tuple +.. |node-subspace| replace:: ``node_subspace`` +.. |content-subspace| replace:: ``content_subspace`` +.. |allow-manual-prefixes| replace:: ``allow_manual_prefixes`` + +.. include:: api-common.rst.inc + +####################### +Python API +####################### + +.. |block| replace:: :ref:`block ` +.. |future-type| replace:: (:ref:`future `) +.. |future-object| replace:: :ref:`Future ` object +.. |infrequent| replace:: *Infrequently used*. +.. |slice-defaults| replace:: The default slice begin is ``''``; the default slice end is ``'\xFF'``. +.. |byte-string| replace:: In Python 2, a byte string is a string of type ``str``. In Python 3, a byte string has type ``bytes``. + +Installation +============ + +The FoundationDB Python API is compatible with Python 2.7 - 3.6. You will need to have a Python version within this range on your system before the FoundationDB Python API can be installed. + +On macOS, the FoundationDB Python API is installed as part of the FoundationDB installation (see :ref:`installing-client-binaries`). On Ubuntu or RHEL/CentOS, you will need to install the FoundationDB Python API manually. + +You can download the FoundationDB Python API source directly from :doc:`downloads`. + +.. note:: The Python language binding is compatible with FoundationDB client binaries of version 2.0 or higher. When used with version 2.0.x client binaries, the API version must be set to 200 or lower. + +After installation, the module ``fdb`` should be usable from your Python installation or path. (The system default python is always used by the client installer on macOS.) + +.. _api-python-versioning: + +API versioning +============== + +When you import the ``fdb`` module, it exposes only one useful symbol: + +.. function:: api_version(version) + + Specifies the version of the API that the application uses. This allows future versions of FoundationDB to make API changes without breaking existing programs. The current version of the API is |api-version|. + +.. note:: You must call ``fdb.api_version(...)`` before using any other part of the API. Once you have done so, the rest of the API will become available in the ``fdb`` module. This requirement includes use of the ``@fdb.transactional`` decorator, which is called when your module is imported. + +.. note:: |api-version-rationale| + +.. warning:: |api-version-multi-version-warning| + +For API changes between version 13 and |api-version| (for the purpose of porting older programs), see :doc:`release-notes`. + +Opening a database +================== + +After importing the ``fdb`` module and selecting an API version, you probably want to open a :class:`Database`. The simplest way of doing this is using :func:`open`:: + + import fdb + fdb.api_version(520) + db = fdb.open() + +.. function:: open( cluster_file=None, db_name="DB", event_model=None ) + + |fdb-open-blurb| + + .. param event_model:: Can be used to select alternate :ref:`api-python-event-models` + + .. note:: In this release, db_name must be "DB". + + .. note:: ``fdb.open()`` combines the effect of :func:`init`, :func:`create_cluster`, and :meth:`Cluster.open_database`. + +.. function:: init() + + Initializes the FoundationDB API, creating a thread for the FoundationDB client and initializing the client's networking engine. :func:`init()` can only be called once. If called subsequently or after :func:`open`, it will raise an ``client_invalid_operation`` error. + +.. function:: create_cluster( cluster_file=None ) + + Connects to the cluster specified by :ref:`cluster_file `, or by a :ref:`default cluster file ` if + ``cluster_file`` is None. :func:`init` must be called first. + + Returns a |future-type| :class:`Cluster` object. + +.. data:: options + + |network-options-blurb| + + .. note:: |network-options-warning| + + .. method :: fdb.options.set_trace_enable( output_directory=None ) + + |option-trace-enable-blurb| + + .. warning:: |option-trace-enable-warning| + + .. method :: fdb.options.set_trace_max_logs_size(bytes) + + |option-trace-max-logs-size-blurb| + + .. method :: fdb.options.set_trace_roll_size(bytes) + + |option-trace-roll-size-blurb| + + .. method :: fdb.options.set_disable_multi_version_client_api() + + |option-disable-multi-version-client-api| + + .. method :: fdb.options.set_callbacks_on_external_threads() + + |option-callbacks-on-external-threads| + + .. method :: fdb.options.set_external_client_library(path_to_lib) + + |option-external-client-library| + + .. method :: fdb.options.set_external_client_directory(path_to_lib_directory) + + |option-external-client-directory| + + .. note:: |tls-options-burb| + + .. method :: fdb.options.set_tls_plugin(plugin_path_or_name) + + |option-tls-plugin-blurb| + + .. method :: fdb.options.set_tls_cert_path(path_to_file) + + |option-tls-cert-path-blurb| + + .. method :: fdb.options.set_tls_key_path(path_to_file) + + |option-tls-key-path-blurb| + + .. method :: fdb.options.set_tls_verify_peers(criteria) + + |option-tls-verify-peers-blurb| + + .. method :: fdb.options.set_tls_cert_bytes(bytes) + + |option-tls-cert-bytes| + + .. method :: fdb.options.set_tls_key_bytes(bytes) + + |option-tls-key-bytes| + +Cluster objects +=============== + +.. class:: Cluster + +.. method:: Cluster.open_database(name="DB") + + Opens a database with the given name. + + Returns a |future-type| :class:`Database` object. + + .. note:: In this release, name **must** be "DB". + +.. _api-python-keys: + +Keys and values +=============== + +|keys-values-blurb| |byte-string| + +|keys-values-other-types-blurb| + +``as_foundationdb_key`` and ``as_foundationdb_value`` +----------------------------------------------------- + +|as-foundationdb-blurb| + +.. warning :: |as-foundationdb-warning| + +KeyValue objects +================ + +.. class :: KeyValue + + Represents a single key-value pair in the database. This is a simple value type; mutating it won't affect your :class:`Transaction` or :class:`Database`. + + KeyValue supports the Python iterator protocol so that you can unpack a key and value directly into two variables:: + + for key, value in tr[begin:end]: + pass + +Attributes +---------- + +.. attribute:: KeyValue.key +.. attribute:: KeyValue.value + +Key selectors +============= + +|keysel-blurb1| + +|keysel-blurb2| + +.. class :: KeySelector(key, or_equal, offset) + + Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods: + +.. classmethod:: KeySelector.last_less_than(key) + + Returns a key selector referencing the last (greatest) key in the database less than the specified key. + +.. classmethod:: KeySelector.last_less_or_equal(key) + + Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. + +.. classmethod:: KeySelector.first_greater_than(key) + + Returns a key selector referencing the first (least) key greater than the specified key. + +.. classmethod:: KeySelector.first_greater_or_equal(key) + + Returns a key selector referencing the first key greater than, or equal to, the specified key. + +``KeySelector + offset`` + Adding an integer ``offset`` to a ``KeySelector`` returns a key selector referencing a key ``offset`` keys after the original ``KeySelector``. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. + +``KeySelector - offset`` + Subtracting an integer ``offset`` from a ``KeySelector`` returns a key selector referencing a key ``offset`` keys before the original ``KeySelector``. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. + +Database objects +================ + +.. class:: Database + +A |database-blurb1| |database-blurb2| + +.. note:: |database-sync| + +.. method:: Database.create_transaction() + + Returns a new :class:`Transaction` object. Consider using the :func:`@fdb.transactional ` decorator to create transactions instead, since it will automatically provide you with appropriate retry behavior. + +.. |sync-read| replace:: This read is fully synchronous. +.. |sync-write| replace:: This change will be committed immediately, and is fully synchronous. + +.. method:: Database.get(key) + + Returns the value associated with the specified key in the database (or ``None`` if the key does not exist). |sync-read| + +``X = db[key]`` + Shorthand for ``X = db.get(key)``. + +.. method:: Database.get_key(key_selector) + + Returns the key referenced by the specified :class:`KeySelector`. |sync-read| + + |database-get-key-caching-blurb| + +.. method:: Database.get_range(begin, end[, limit, reverse, streaming_mode]) + + Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as a list of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. |sync-read| + + Each of ``begin`` and ``end`` may be a key or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. + + If ``limit`` is specified, then only the first ``limit`` keys (and their values) in the range will be returned. + + If ``reverse`` is True, then the last ``limit`` keys in the range will be returned in reverse order. + + If ``streaming_mode`` is specified, it must be a value from the :data:`StreamingMode` enumeration. It provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. + +``X = db[begin:end]`` + Shorthand for ``X = db.get_range(begin, end)``. |slice-defaults| + +``X = db[begin:end:-1]`` + Shorthand for ``X = db.get_range(begin, end, reverse=True)``. |slice-defaults| + +.. method:: Database.get_range_startswith(prefix[, limit, reverse, streaming_mode]) + + Returns all keys ``k`` such that ``k.startswith(prefix)``, and their associated values, as a list of :class:`KeyValue` objects. The ``limit``, ``reverse`` and ``streaming_mode`` parameters have the same meanings as in :meth:`Database.get_range()`. + +.. method:: Database.set(key, value) + + Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |sync-write| + +``db[key] = value`` + Shorthand for ``db.set(key, value)``. + +.. method:: Database.clear(key) + + Removes the specified key (and any associated value), if it exists. |sync-write| + +``del db[key]`` + Shorthand for ``db.clear(key)``. + +.. method:: Database.clear_range(begin, end) + + Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |sync-write| + +``del db[begin:end]`` + Shorthand for ``db.clear_range(begin, end)``. |slice-defaults| + +.. method:: Database.clear_range_startswith(prefix) + + Removes all keys ``k`` such that ``k.startswith(prefix)``, and their associated values. |sync-write| + +.. method:: Database.get_and_watch(key) + + Returns a tuple ``value, watch``, where ``value`` is the value associated with ``key`` or ``None`` if the key does not exist, and ``watch`` is a :class:`FutureVoid` that will become ready after ``value`` changes. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.set_and_watch(key, value) + + Sets ``key`` to ``value`` and returns a :class:`FutureVoid` that will become ready after a subsequent change to ``value``. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.clear_and_watch(key) + + Removes ``key`` (and any associated value) if it exists and returns a :class:`FutureVoid` that will become ready after the value is subsequently set. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.add(key, param) +.. method:: Database.bit_and(key, param) +.. method:: Database.bit_or(key, param) +.. method:: Database.bit_xor(key, param) + + These atomic operations behave exactly like the :ref:`associated operations ` on :class:`Transaction` objects except that the change will immediately be committed, and is fully synchronous. + + .. note :: |database-atomic-ops-idempotency-note| + +.. _api-python-database-options: + +Database options +---------------- + +|database-options-blurb| + +.. method:: Database.options.set_location_cache_size(size) + + |option-location-cache-size-blurb| + +.. method:: Database.options.set_max_watches(max_watches) + + |option-max-watches-blurb| + +.. method:: Database.options.set_machine_id(id) + + |option-machine-id-blurb| + +.. method:: Database.options.set_datacenter_id(id) + + |option-datacenter-id-blurb| + +.. _api-python-transactional-decorator: + +Transactional decoration +======================== + +.. decorator:: transactional() + + The ``@fdb.transactional`` decorator is a convenience designed to concisely wrap a function with logic to automatically create a transaction and retry until success. + + For example:: + + @fdb.transactional + def simple_function(tr, x, y): + tr[b'foo'] = x + tr[b'bar'] = y + + The ``@fdb.transactional`` decorator makes ``simple_function`` a transactional function. All functions using this decorator must have an argument **named** ``tr``. This specially named argument is passed a transaction that the function can use to do reads and writes. + + A caller of a transactionally decorated function can pass a :class:`Database` instead of a transaction for the ``tr`` parameter. Then a transaction will be created automatically, and automatically committed before returning to the caller. The decorator will retry calling the decorated function until the transaction successfully commits. + + If ``db`` is a :class:`Database`, a call like :: + + simple_function(db, 'a', 'b') + + is equivalent to something like :: + + tr = db.create_transaction() + while True: + try: + simple_function(tr, 'a', 'b') + tr.commit().wait() + break + except fdb.FDBError as e: + tr.on_error(e).wait() + + A caller may alternatively pass an actual transaction to the ``tr`` parameter. In this case, the transactional function will not attempt to commit the transaction or to retry errors, since that is the responsibility of the caller who owns the transaction. This design allows transactionally decorated functions to be composed freely into larger transactions. + + .. note :: |fdb-transactional-unknown-result-note| + +Transaction objects +=================== + +.. class:: Transaction + +A ``Transaction`` object represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a ``Transaction``. + +|transaction-blurb1| + +|transaction-blurb2| + +|transaction-blurb3| + +The most convenient way to use Transactions is using the :func:`@fdb.transactional ` decorator. + +Keys and values in FoundationDB are byte strings (``str`` in Python 2.x, ``bytes`` in 3.x). To encode other data types, see the :mod:`fdb.tuple` module and :ref:`encoding-data-types`. + +Attributes +---------- + +.. attribute:: Transaction.db + + |db-attribute-blurb| + +Reading data +------------ + +.. method:: Transaction.get(key) + + Returns a |future-type| :class:`Value` associated with the specified key in the database. + + To check whether the specified key was present in the database, call :meth:`Value.present()` on the return value. + +``X = tr[key]`` + Shorthand for ``X = tr.get(key)``. + +.. method:: Transaction.get_key(key_selector) + + Returns the |future-type| :class:`Key` referenced by the specified :class:`KeySelector`. + + |transaction-get-key-caching-blurb| + +.. method:: Transaction.get_range(begin, end[, limit, reverse, streaming_mode]) + + Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an iterator yielding :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. + + Like a |future-object|, the returned iterator issues asynchronous read operations. It fetches the data in one or more efficient batches (depending on the value of the ``streaming_mode`` parameter). However, the iterator will block if iteration reaches a value whose read has not yet completed. + + Each of ``begin`` and ``end`` may be a key or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. + + If ``limit`` is specified, then only the first ``limit`` keys (and their values) in the range will be returned. + + If ``reverse`` is True, then the last ``limit`` keys in the range will be returned in reverse order. + + If ``streaming_mode`` is specified, it must be a value from the :data:`StreamingMode` enumeration. It provides a hint to FoundationDB about how the returned container is likely to be used. The default is :data:`StreamingMode.iterator`. + +``X = tr[begin:end]`` + Shorthand for ``X = tr.get_range(begin, end)``. |slice-defaults| + +``X = tr[begin:end:-1]`` + Shorthand for ``X = tr.get_range(begin, end, reverse=True)``. |slice-defaults| + +.. method:: Transaction.get_range_startswith(prefix[, limit, reverse, streaming_mode]) + + Returns all keys ``k`` such that ``k.startswith(prefix)``, and their associated values, as a container of :class:`KeyValue` objects (see :meth:`Transaction.get_range()` for a description of the returned container). + + The ``limit``, ``reverse`` and ``streaming_mode`` parameters have the same meanings as in :meth:`Transaction.get_range()`. + +.. _api-python-snapshot-reads: + +Snapshot reads +-------------- + +.. attribute:: Transaction.snapshot + + |snapshot-blurb1| + + |snapshot-blurb2| + + |snapshot-blurb3| + + |snapshot-blurb4| + +.. attribute:: Transaction.snapshot.db + + |db-attribute-blurb| + +.. method:: Transaction.snapshot.get(key) + + Like :meth:`Transaction.get`, but as a snapshot read. + +``X = tr.snapshot[key]`` + Shorthand for ``X = tr.snapshot.get(key)``. + +.. method:: Transaction.snapshot.get_key(key_selector) -> key + + Like :meth:`Transaction.get_key`, but as a snapshot read. + +.. method:: Transaction.snapshot.get_range(begin, end[, limit, reverse, streaming_mode]) + + Like :meth:`Transaction.get_range`, but as a snapshot read. + +``X = tr.snapshot[begin:end]`` + Shorthand for ``X = tr.snapshot.get_range(begin, end)``. |slice-defaults| + +``X = tr.snapshot[begin:end:-1]`` + Shorthand for ``X = tr.snapshot.get_range(begin, end, reverse=True)``. |slice-defaults| + +.. method:: Transaction.snapshot.get_range_startswith(prefix[, limit, reverse, streaming_mode]) + + Like :meth:`Transaction.get_range_startswith`, but as a snapshot read. + +.. method:: Transaction.snapshot.get_read_version() + + Identical to :meth:`Transaction.get_read_version` (since snapshot and serializable reads use the same read version). + + +Writing data +------------ + +.. |immediate-return| replace:: Returns immediately, having modified the snapshot represented by this :class:`Transaction`. + +.. method:: Transaction.set(key,value) + + Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |immediate-return| + +``tr[key] = value`` + Shorthand for ``tr.set(key,value)``. + +.. method:: Transaction.clear(key) + + Removes the specified key (and any associated value), if it exists. |immediate-return| + +``del tr[key]`` + Shorthand for ``tr.clear(key)``. + +.. method:: Transaction.clear_range(begin, end) + + Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |immediate-return| + + .. note :: Unlike in the case of :meth:`get_range`, ``begin`` and ``end`` must be keys (byte strings), not :class:`KeySelector`\ s. (Resolving arbitrary key selectors would prevent this method from returning immediately, introducing concurrency issues.) + +``del tr[begin:end]`` + Shorthand for ``tr.clear_range(begin,end)``. |slice-defaults| + +.. method:: Transaction.clear_range_startswith(prefix) + + Removes all the keys ``k`` such that ``k.startswith(prefix)``, and their associated values. |immediate-return| + +.. _api-python-transaction-atomic-operations: + +Atomic operations +----------------- + +|atomic-ops-blurb1| + +|atomic-ops-blurb2| + +|atomic-ops-blurb3| + +.. warning :: |atomic-ops-warning| + +In each of the methods below, ``param`` should be a string appropriately packed to represent the desired value. For example:: + + # wrong + tr.add('key', 1) + + # right + import struct + tr.add('key', struct.pack('` for explanation and examples. + +.. method :: Transaction.commit() + + Attempt to commit the changes made in the transaction to the database. Returns a :class:`FutureVoid` representing the asynchronous result of the commit. You **must** call the :meth:`Future.wait()` method on the returned :class:`FutureVoid`, which will raise an exception if the commit failed. + + |commit-unknown-result-blurb| + + |commit-outstanding-reads-blurb| + + .. note :: Consider using the :func:`@fdb.transactional ` decorator, which not only calls :meth:`Database.create_transaction` and :meth:`Transaction.commit()` for you but also implements the required error handling and retry logic for transactions. + + .. warning :: |used-during-commit-blurb| + +.. method :: Transaction.on_error(exception) + + Determine whether an exception raised by a :class:`Transaction` method is retryable. Returns a :class:`FutureVoid`. You **must** call the :meth:`Future.wait()` method on the :class:`FutureVoid`, which will return after a delay if the exception was retryable, or re-raise the exception if it was not. + + .. note :: Consider using the :func:`@fdb.transactional ` decorator, which calls this method for you. + +.. method :: Transaction.reset() + + |transaction-reset-blurb| + +.. _api-python-cancel: + +.. method :: Transaction.cancel() + + |transaction-cancel-blurb| + + .. warning :: |transaction-reset-cancel-warning| + + .. warning :: |transaction-commit-cancel-warning| + +.. _api-python-watches: + +Watches +------- + +.. method:: Transaction.watch(key) + + Creates a watch and returns a :class:`FutureVoid` that will become ready when the watch reports a change to the value of the specified key. + + |transaction-watch-blurb| + + |transaction-watch-committed-blurb| + + |transaction-watch-error-blurb| + + |transaction-watch-limit-blurb| + +.. _api-python-conflict-ranges: + +Conflict ranges +--------------- + +.. note:: |conflict-range-note| + +|conflict-range-blurb| + +.. method:: Transaction.add_read_conflict_range(begin, end) + + |add-read-conflict-range-blurb| + +.. method:: Transaction.add_read_conflict_key(key) + + |add-read-conflict-key-blurb| + +.. method:: Transaction.add_write_conflict_range(begin, end) + + |add-write-conflict-range-blurb| + +.. method:: Transaction.add_write_conflict_key(key) + + |add-write-conflict-key-blurb| + +Versions +-------- + +Most applications should use the read version that FoundationDB determines automatically during the transaction's first read, and ignore all of these methods. + +.. method :: Transaction.set_read_version(version) + + |infrequent| Sets the database version that the transaction will read from the database. The database cannot guarantee causal consistency if this method is used (the transaction's reads will be causally consistent only if the provided read version has that property). + +.. method :: Transaction.get_read_version() + + |infrequent| Returns a :class:`FutureVersion` representing the transaction's |future-type| read version. You must call the :meth:`Future.wait()` method on the returned object to retrieve the version as an integer. + +.. method :: Transaction.get_committed_version() + + |infrequent| |transaction-get-committed-version-blurb| + +.. method :: Transaction.get_versionstamp() + + |infrequent| |transaction-get-versionstamp-blurb| + +.. _api-python-transaction-options: + +Transaction options +------------------- + +|transaction-options-blurb| + +.. _api-python-snapshot-ryw: + +.. method:: Transaction.options.set_snapshot_ryw_disable + + |option-snapshot-ryw-disable-blurb| + +.. method:: Transaction.options.set_snapshot_ryw_enable + + |option-snapshot-ryw-enable-blurb| + +.. method:: Transaction.options.set_priority_batch + + |option-priority-batch-blurb| + +.. method:: Transaction.options.set_priority_system_immediate + + |option-priority-system-immediate-blurb| + + .. warning:: |option-priority-system-immediate-warning| + +.. method:: Transaction.options.set_causal_read_risky + + |option-causal-read-risky-blurb| + +.. method:: Transaction.options.set_causal_write_risky + + |option-causal-write-risky-blurb| + +.. _api-python-no-write-conflict-range: + +.. method:: Transaction.options.set_next_write_no_write_conflict_range + + |option-next-write-no-write-conflict-range-blurb| + + .. note:: |option-next-write-no-write-conflict-range-note| + +.. method:: Transaction.options.set_read_your_writes_disable + + |option-read-your-writes-disable-blurb| + + .. note:: |option-read-your-writes-disable-note| + +.. method:: Transaction.options.set_read_ahead_disable + + |option-read-ahead-disable-blurb| + +.. method:: Transaction.options.set_access_system_keys + + |option-access-system-keys-blurb| + + .. warning:: |option-access-system-keys-warning| + +.. method:: Transaction.options.set_read_system_keys + + |option-read-system-keys-blurb| + + .. warning:: |option-read-system-keys-warning| + +.. method:: Transaction.options.set_retry_limit + + |option-set-retry-limit-blurb1| + + |option-set-retry-limit-blurb2| + +.. method:: Transaction.options.set_max_retry_delay + + |option-set-max-retry-delay-blurb| + +.. _api-python-timeout: + +.. method:: Transaction.options.set_timeout + + |option-set-timeout-blurb1| + + |option-set-timeout-blurb2| + + |option-set-timeout-blurb3| + +.. _api-python-future: + +Future objects +============== + +|future-blurb1| + +.. _api-python-block: + +When a future object "blocks", what actually happens is determined by the :ref:`event model `. A threaded program will block a thread, but a program using the gevent model will block a greenlet. + +All future objects are a subclass of the :class:`Future` type. + +.. class:: Future + +.. method:: Future.wait() + + Blocks until the object is ready, and returns the object value (or raises an exception if the asynchronous function failed). + +.. method:: Future.is_ready() + + Immediately returns true if the future object is ready, false otherwise. + +.. method:: Future.block_until_ready() + + Blocks until the future object is ready. + +.. method:: Future.on_ready(callback) + + Calls the specified callback function, passing itself as a single argument, when the future object is ready. If the future object is ready at the time :meth:`on_ready()` is called, the call may occur immediately in the current thread (although this behavior is not guaranteed). Otherwise, the call may be delayed and take place on the thread with which the client was initialized. Therefore, the callback is responsible for any needed thread synchronization (and/or for posting work to your application's event loop, thread pool, etc., as may be required by your application's architecture). + +.. warning:: |fdb-careful-with-callbacks-blurb| + +.. method:: Future.cancel() + + |future-cancel-blurb| + +.. staticmethod:: Future.wait_for_any(*futures) + + Does not return until at least one of the given future objects is ready. Returns the index in the parameter list of a ready future object. + +Asynchronous methods return one of the following subclasses of :class:`Future`: + +.. class:: Value + + Represents a future string object and responds to the same methods as string in Python. They may be passed to FoundationDB methods that expect a string. + +.. method:: Value.present() + + Returns ``False`` if the key used to request this value was not present in the database. For example:: + + @fdb.transactional + def foo(tr): + val = tr[b'foo'] + if val.present(): + print 'Got value: %s' % val + else: + print 'foo was not present' + +.. class:: Key + + Represents a future string object and responds to the same methods as string in Python. They may be passed to FoundationDB methods that expect a string. + +.. class:: FutureVersion + + Represents a future version (integer). You must call the :meth:`Future.wait()` method on this object to retrieve the version as an integer. + +.. class:: FutureStringArray + + Represents a future list of strings. You must call the :meth:`Future.wait()` method on this object to retrieve the list of strings. + +.. class:: FutureVoid + + Represents a future returned from asynchronous methods that logically have no return value. + + For a :class:`FutureVoid` object returned by :meth:`Transaction.commit()` or :meth:`Transaction.on_error()`, you must call the :meth:`Future.wait()` method, which will either raise an exception if an error occurred during the asynchronous call, or do nothing and return ``None``. + +.. _streaming-mode-python: + +Streaming modes +=============== + +.. data:: StreamingMode + +|streaming-mode-blurb1| + +|streaming-mode-blurb2| + +The following streaming modes are available: + +.. data:: StreamingMode.iterator + + *The default.* The client doesn't know how much of the range it is likely to used and wants different performance concerns to be balanced. + + Only a small portion of data is transferred to the client initially (in order to minimize costs if the client doesn't read the entire range), and as the caller iterates over more items in the range larger batches will be transferred in order to maximize throughput. + +.. data:: StreamingMode.want_all + + The client intends to consume the entire range and would like it all transferred as early as possible. + +.. data:: StreamingMode.small + + |infrequent| Transfer data in batches small enough to not be much more expensive than reading individual rows, to minimize cost if iteration stops early. + +.. data:: StreamingMode.medium + + |infrequent| Transfer data in batches sized in between small and large. + +.. data:: StreamingMode.large + + |infrequent| Transfer data in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the client stops iteration early, some disk and network bandwidth may be wasted. The batch size may still be too small to allow a single client to get high throughput from the database, so if that is what you need consider :data:`StreamingMode.serial`. + +.. data:: StreamingMode.serial + + Transfer data in batches large enough that an individual client can get reasonable read bandwidth from the database. If the client stops iteration early, considerable disk and network bandwidth may be wasted. + +.. data:: StreamingMode.exact + + |infrequent| The client has passed a specific row limit and wants that many rows delivered in a single batch. This is not particularly useful in Python because iterator functionality makes batches of data transparent, so use :data:`StreamingMode.want_all` instead. + + +.. _api-python-event-models: + +Event models +============ + +By default, the FoundationDB Python API assumes that the calling program uses threads (as provided by the ``threading`` module) for concurrency. This means that blocking operations will block the current Python thread. This behavior can be changed by specifying the optional ``event_model`` parameter to the :func:`open` or :func:`init` functions. + +The following event models are available: + +``event_model=None`` + The default. Blocking operations will block the current Python thread. This is also fine for programs without any form of concurrency. + +``event_model="gevent"`` + The calling program uses the `gevent `_ module for single-threaded concurrency. Blocking operations will block the current greenlet. + + The FoundationDB Python API has been tested with gevent versions 0.13.8 and 1.0rc2 and should work with all gevent 0.13 and 1.0 releases. + + .. note:: The ``gevent`` event model on Windows requires gevent 1.0 or newer. + +``event_model="debug"`` + The calling program is threaded, but needs to be interruptible (by Ctrl-C). Blocking operations will poll, effectively blocking the current thread but responding to keyboard interrupts. This model is inefficient, but can be very useful for debugging. + +.. _api-python-release-notes: + +Errors +====== + +Errors in the FoundationDB API are raised as exceptions of type :class:`FDBError`. These errors may be displayed for diagnostic purposes, but generally should be passed to :meth:`Transaction.on_error`. When using :func:`@fdb.transactional `, appropriate errors will be retried automatically. + +.. class:: FDBError + +.. attribute:: FDBError.code + + An integer associated with the error type. + +.. attribute:: FDBError.description + + A somewhat human-readable description of the error. + +.. warning:: You should use only :attr:`FDBError.code` for programmatic comparisons, as the description of the error may change at any time. Whenever possible, use the :meth:`Transaction.on_error()` method to handle :class:`FDBError` exceptions. + +.. _api-python-tuple-layer: + +Tuple layer +=========== + +.. module:: fdb.tuple + +|tuple-layer-blurb| + +.. note:: |tuple-layer-note| + +The tuple layer in the FoundationDB Python API supports tuples that contain elements of the following data types: + ++-----------------------+-------------------------------------------------------------------------------+ +| Type | Legal Values | ++=======================+===============================================================================+ +| ``None`` | Any ``value`` such that ``value == None`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Byte string | Any ``value`` such that ``isinstance(value, bytes)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Unicode string | Any ``value`` such that ``isinstance(value, unicode)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Integer | Python 2.7: Any ``value`` such that ``isinstance(value, (int,long))`` and | +| | ``-2**2040+1 <= value <= 2**2040-1``. Python 3.x: Any ``value`` such that | +| | ``isinstance(value, int)`` and ``-2**2040+1 <= value <= 2**2040-1``. | ++-----------------------+-------------------------------------------------------------------------------+ +| Floating point number | Any ``value`` such that ``isinstance(value, fdb.tuple.SingleFloat)`` or | +| (single-precision) | ``isinstance(value, ctypes.c_float)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Floating point number | Any ``value`` such that ``isinstance(value, (ctypes.c_double, float))`` | +| (double-precision) | | ++-----------------------+-------------------------------------------------------------------------------+ +| Boolean | Any ``value`` such that ``isinstance(value, Boolean)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| UUID | Any ``value`` such that ``isinstance(value, uuid.UUID)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Versionstamp | Any ``value`` such that ``isinstance(value, fdb.tuple.Versionstamp)`` | ++-----------------------+-------------------------------------------------------------------------------+ +| Tuple or List | Any ``value`` such that ``isinstance(value, (tuple, list))`` and each element | +| | within ``value`` is one of the supported types with a legal value. | ++-----------------------+-------------------------------------------------------------------------------+ + +If ``T`` is a Python tuple meeting these criteria, then:: + + fdb.tuple.compare(T, fdb.tuple.unpack(fdb.tuple.pack(T))) == 0 + +That is, any tuple meeting these criteria will have the same semantic value if serialized and deserialized. For +the most part, this also implies that ``T == fdb.tuple.unpack(fdb.tuple.pack(T))`` with the following caveats: + +- Any ``value`` of type ``ctypes.c_double`` is converted to the Python ``float`` type, but + ``value.value == fdb.tuple.unpack(fdb.tuple.pack((value,)))[0]`` will be true (as long as ``value`` is not NaN). +- Any ``value`` of type ``ctypes.c_float`` is converted into a ``fdb.tuple.SingleFloat`` instance, but + ``value.value == fdb.tuple.unpack(fdb.tuple.pack((value,)))[0].value`` will be true (as long as ``value.value`` is not NaN). +- Any ``value`` of type ``list`` or ``tuple`` is converted to a ``tuple`` type where the elements of the serialized and deserialized ``value`` + will be equal (subject to these caveats) to the elements of the original ``value``. + +``import fdb.tuple`` + Imports the FoundationDB tuple layer. + +.. method:: pack(tuple, prefix=b'') + + Returns a key (byte string) encoding the specified tuple. If ``prefix`` is set, it will prefix the serialized + bytes with the prefix string. This throws an error if any of the tuple's items are incomplete `Versionstamp` + instances. + +.. method:: pack_with_versionstamp(tuple, prefix=b'') + + Returns a key (byte string) encoding the specified tuple. This method will throw an error unless + exactly one of the items of the tuple is an incomplete :class:`Versionstamp` instance. (It will + recurse down nested tuples if there are any to find one.) If so, it will produce a byte string + that can be fed into :meth:`fdb.Transaction.set_versionstamped_key` and correctly fill in the + versionstamp information at commit time so that when the key is re-read and deserialized, the + only difference is that the `Versionstamp` instance is complete and has the transaction version + filled in. This throws an error if there are no incomplete `Versionstamp` instances in the tuple + or if there is more than one. + +.. method:: unpack(key) + + Returns the tuple encoded by the given key. + +.. method:: has_incomplete_versionstamp(tuple) + + Returns ``True`` if there is at least one element contained within the tuple that is a + :class`Versionstamp` instance that is incomplete. If there are multiple incomplete + :class:`Versionstamp` instances, this method will return ``True``, but trying to pack it into a + byte string will result in an error. + +.. method:: range(tuple) + + Returns a Python slice object representing all keys that encode tuples strictly starting with ``tuple`` (that is, all tuples of greater length than tuple of which tuple is a prefix). + + Can be used to directly index a Transaction object to retrieve a range. For example:: + + tr[ fdb.tuple.range(('A',2)) ] + + returns all key-value pairs in the database whose keys would unpack to tuples like ('A', 2, x), ('A', 2, x, y), etc. + +.. method:: compare(tuple1, tuple2) + + Compares two tuples in a way that respects the natural ordering of the elements within the tuples. It will + return -1 if ``tuple1`` would sort before ``tuple2`` when performing an element-wise comparison of the two + tuples, it will return 1 if ``tuple1`` would sort after ``tuple2``, and it will return 0 if the two + tuples are equivalent. If the function must compare two elements of different types while doing the comparison, + it will sort the elements based on their internal type codes, so comparisons are consistent if not necessarily + semantically meaningful. Strings are sorted on their byte representation when encoded into UTF-8 (which may + differ from the default sort when non-ASCII characters are included within the string), and UUIDs are sorted + based on their big-endian byte representation. Single-precision floating point numbers are sorted before all + double-precision floating point numbers, and for floating point numbers, -NaN is sorted before -Infinity which + is sorted before finite numbers which are sorted before Infinity which is sorted before NaN. Different representations + of NaN are not treated as equal. + + Additionally, the tuple serialization contract is such that after they are serialized, the byte-string representations + of ``tuple1`` and ``tuple2`` will sort in a manner that is consistent with this function. In particular, this function + obeys the following contract:: + + fdb.tuple.compare(tuple1, tuple2) == -1 if fdb.tuple.pack(tuple1) < fdb.tuple.pack(tuple2) else \ + 0 if fdb.tuple.pack(tuple2) == fdb.tuple.pack(tuple2) else 1 + + As byte order is the comparator used within the database, this comparator can be used to determine the order + of keys within the database. + +.. class:: SingleFloat(value) + + Wrapper around a single-precision floating point value. When constructed, the ``value`` parameter should either be + an integral value, a ``float``, or a ``ctypes.c_float``. It will then properly store the value in its + :attr:`SingleFloat.value` field (which should not be mutated). If the float does not fit within a IEEE 754 floating point + integer, there may be a loss of precision. + +.. attribute:: SingleFloat.value + + The underlying value of the ``SingleFloat`` object. This will have type ``float``. + +.. method:: SingleFloat.__eq__(other) +.. method:: SingleFloat.__ne__(other) +.. method:: SingleFloat.__lt__(other) +.. method:: SingleFloat.__le__(other) +.. method:: SingleFloat.__gt__(other) +.. method:: SingleFloat.__ge__(other) + + Comparison functions for ``SingleFloat`` objects. This will sort according to the byte representation + of the object rather than using standard float comparison. In particular, this means that ``-0.0 != 0.0`` + and that the ``NaN`` values will sort in a way that is consistent with the :meth:`compare` method between + tuples rather than using standard floating-point comparison. + +.. class:: Versionstamp(tr_version=None, user_version=0) + + Used to represent values written by versionstamp operations within the tuple layer. This + wraps a single byte array of length 12 that can be used to represent some global order + of items within the database. These versions are composed of two separate components: + (1) the 10-byte ``tr_version`` and (2) the two-byte ``user_version``. The ``tr_version`` + is set by the database, and it is used to impose an order between different transactions. + This order is guaranteed to be monotonically increasing over time for a given database. + (In particular, it imposes an order that is consistent with a serialization order of + the database's transactions.) If the client elects to leave the ``tr_version`` as its + default value of ``None``, then the ``Versionstamp`` is considered "incomplete". This + will cause the first 10 bytes of the serialized ``Versionstamp`` to be filled in with + dummy bytes when serialized. When used with :meth:`fdb.Transaction.set_versionstamped_key`, + an incomplete version can be used to ensure that a key gets written with the + current transaction's version which can be useful for maintaining append-only data + structures within the database. If the ``tr_version`` is set to something that is + not ``None``, it should be set to a byte array of length 10. In this case, the + ``Versionstamp`` is considered "complete". This is the usual case when one + reads a serialized ``Versionstamp`` from the database. + + The ``user_version`` should be specified as an integer, but it must fit within a two-byte + unsigned integer. It is set by the client, and it is used to impose an order between items + serialized within a single transaction. If left unset, then final two bytes of the serialized + ``Versionstamp`` are filled in with a default (constant) value. + + Sample usage of this class might be something like this:: + + @fdb.transactional + def write_versionstamp(tr, prefix): + tr.set_versionstamped_key(fdb.tuple.pack_with_versionstamp((prefix, fdb.tuple.Versionstamp())), b'') + return tr.get_versionstamp() + + @fdb.transactional + def read_versionstamp(tr, prefix): + subspace = fdb.Subspace((prefix,)) + for k, _ in tr.get_range(subspace.range().start, subspace.range().stop, 1): + return subspace.unpack(k)[0] + return None + + db = fdb.open() + del db[fdb.tuple.range(('prefix',))] + tr_version = write_versionstamp(db, 'prefix').wait() + v = read_versionstamp(db, 'prefix') + assert v == fdb.tuple.Versionstamp(tr_version=tr_version) + + Here, we serialize an incomplete ``Versionstamp`` and then write it using the ``set_versionstamped_key`` + mutation so that it picks up the transaction's version information. Then when we read it + back, we get a complete ``Versionstamp`` with the committed transaction's version. + +.. attribute:: Versionstamp.tr_version + + The inter-transaction component of the ``Versionstamp`` class. It should be either ``None`` (to + indicate an incomplete ``Versionstamp`` that will set the version later) or to some 10 byte value + indicating the commit version and batch version of some transaction. + +.. attribute:: Versionstamp.user_version + + The intra-transaction component of the ``Versionstamp`` class. It should be some number that can + fit within two bytes (i.e., between 0 and 65,535 inclusive). It can be used to impose an order + between items that are committed together in the same transaction. If left unset, then + the versionstamp is assigned a (constant) default user version value. + +.. method:: Versionstamp.from_bytes(bytes) + + Static initializer for ``Versionstamp`` instances that takes a serialized ``Versionstamp`` and + creates an instance of the class. The ``bytes`` parameter should be a byte string of length 12. + This method will serialize the version as a "complete" ``Versionstamp`` unless the dummy bytes + are equal to the default transaction version assigned to incomplete ``Versionstamps``. + +.. method:: Versionstamp.is_complete() + + Returns whether this version has been given a (non-``None``) ``tr_version`` or not. + +.. method:: Versionstamp.completed(tr_version) + + If this ``Versionstamp`` is incomplete, this returns a copy of this instance except that the + ``tr_version`` is filled in with the passed parameter. If the ``Versionstamp`` is already + complete, it will raise an error. + +.. method:: Versionstamp.to_bytes() + + Produces a serialized byte string corresponding to this versionstamp. It will have length 12 and + will combine the ``tr_version`` and ``user_version`` to produce a byte string that + lexicographically sorts appropriately with other ``Versionstamp`` instances. If this instance is + incomplete, then the ``tr_version`` component gets filled in with dummy bytes that will cause it + to sort after every complete ``Verionstamp``'s serialized bytes. + +.. method:: Versionstamp.__eq__(other) +.. method:: Versionstamp.__ne__(other) +.. method:: Versionstamp.__lt__(other) +.. method:: Versionstamp.__le__(other) +.. method:: Versionstamp.__gt__(other) +.. method:: Versionstamp.__ge__(other) + + Comparison functions for ``Versionstamp`` objects. For two complete ``Versionstamps``, the + ordering is first lexicographically by ``tr_version`` and then by ``user_version``. + Incomplete ``Versionstamps`` are defined to sort after all complete ``Versionstamps`` (the idea + being that for a given transaction, if a ``Versionstamp`` has been created as the result of + some prior transaction's work, then the incomplete ``Versionstamp``, when assigned a version, + will be assigned a greater version than the existing one), and for two incomplete ``Versionstamps``, + the order is by ``user_version`` only. + +.. _api-python-subspaces: + +Subspaces +========= + +.. currentmodule:: fdb + +|subspace-blurb1| + +|subspace-blurb2| + +.. note:: For general guidance on subspace usage, see the discussion in the :ref:`Developer Guide `. + +.. class:: Subspace(prefixTuple=tuple(), rawPrefix="") + + |subspace-blurb3| + +.. method:: Subspace.key() + + |subspace-key-blurb| + +.. method:: Subspace.pack(tuple=tuple()) + + |subspace-pack-blurb| + +.. method:: Subspace.pack_with_versionstamp(tuple) + + Returns the key encoding the specified tuple in the subspace so that it may be used as the key in the + :meth:`fdb.Transaction.set_versionstampe_key` method. The passed tuple must contain exactly one incomplete + :class:`fdb.tuple.Versionstamp` instance or the method will raise an error. The behavior here is the same + as if one used the :meth:`fdb.tuple.pack_with_versionstamp` method to appropriately pack together + this subspace and the passed tuple. + +.. method:: Subspace.unpack(key) + + |subspace-unpack-blurb| + +.. method:: Subspace.range(tuple=tuple()) + + |subspace-range-blurb| + + The range will be returned as a Python slice object, and may be used with any FoundationDB methods that require a range:: + + r = subspace.range(('A', 2)) + rng_itr1 = tr[r] + rng_itr2 = tr.get_range(r.start, r.stop, limit=1) + +.. method:: Subspace.contains(key) + + |subspace-contains-blurb| + +.. method:: Subspace.as_foundationdb_key() + + |subspace-as-foundationdb-key-blurb| This method serves to support the :ref:`as_foundationdb_key() ` convenience interface. + +.. method:: Subspace.subspace(tuple) + + |subspace-subspace-blurb| + + ``x = subspace[item]`` + + Shorthand for ``x = subspace.subspace((item,))``. This function can be combined with the :meth:`Subspace.as_foundationdb_key()` convenience to turn this:: + + s = fdb.Subspace(('x',)) + tr[s.pack(('foo', 'bar', 1))] = '' + + into this:: + + s = fdb.Subspace(('x',)) + tr[s['foo']['bar'][1]] = '' + +.. _api-python-directories: + +Directories +=========== + +|directory-blurb1| + +.. note:: For general guidance on directory usage, see the discussion in the :ref:`Developer Guide `. + +|directory-blurb2| + +|directory-blurb3| + +.. data:: directory + + The default instance of :class:`DirectoryLayer`. + +.. class:: DirectoryLayer(node_subspace=Subspace(rawPrefix="\xfe"), content_subspace=Subspace(), allow_manual_prefixes=False) + + |directory-layer-blurb| + +.. method:: DirectoryLayer.create_or_open(tr, path, layer=None) + + |directory-create-or-open-blurb| + + If the byte string ``layer`` is specified and the directory is new, it is recorded as the layer; if ``layer`` is specified and the directory already exists, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.open(tr, path, layer=None) + + |directory-open-blurb| + + If the byte string ``layer`` is specified, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.create(tr, path, layer=None, prefix=None) + + |directory-create-blurb| + + If the byte string ``prefix`` is specified, the directory is created with the given physical prefix; otherwise a prefix is allocated automatically. + + If the byte string ``layer`` is specified, it is recorded with the directory and will be checked by future calls to open. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.move(tr, old_path, new_path) + + |directory-move-blurb| + + |directory-move-return-blurb| + +.. method:: DirectoryLayer.remove(tr, path) + + |directory-remove-blurb| + + .. warning:: |directory-remove-warning| + +.. method:: DirectoryLayer.remove_if_exists(tr, path) + + |directory-remove-if-exists-blurb| + + .. warning:: |directory-remove-warning| + +.. method:: DirectoryLayer.list(tr, path=()) + + Returns a list of names of the immediate subdirectories of the directory at ``path``. Each name is a unicode string representing the last component of a subdirectory's path. + +.. method:: DirectoryLayer.exists(tr, path) + + |directory-exists-blurb| + +.. method:: DirectoryLayer.get_layer() + + |directory-get-layer-blurb| + +.. method:: DirectoryLayer.get_path() + + |directory-get-path-blurb| + +.. _api-python-directory-subspace: + +DirectorySubspace +----------------- + +|directory-subspace-blurb| + +.. method:: DirectorySubspace.move_to(tr, new_path) + + |directory-move-to-blurb| + + |directory-move-return-blurb| + +.. _api-python-locality: + +Locality information +==================== + +.. module:: fdb.locality + +|locality-api-blurb| + +.. method:: fdb.locality.get_boundary_keys(db_or_tr, begin, end) + + |locality-get-boundary-keys-blurb| + + |locality-get-boundary-keys-db-or-tr| + + Like a |future-object|, the returned container issues asynchronous read operations to fetch the data in the range and may block while iterating over its values if the read has not completed. + + |locality-get-boundary-keys-warning-danger| + +.. method:: fdb.locality.get_addresses_for_key(tr, key) + + Returns a :class:`fdb.FutureStringArray`. You must call the :meth:`fdb.Future.wait()` method on this object to retrieve a list of public network addresses as strings, one for each of the storage servers responsible for storing ``key`` and its associated value. diff --git a/documentation/sphinx/source/api-ruby.rst b/documentation/sphinx/source/api-ruby.rst index 15184379994..7fdc8fce4c7 100644 --- a/documentation/sphinx/source/api-ruby.rst +++ b/documentation/sphinx/source/api-ruby.rst @@ -1,1261 +1,1257 @@ -.. default-domain:: rb -.. highlight:: ruby -.. module:: FDB - -.. |database-type| replace:: ``Database`` -.. |database-class| replace:: :class:`Database` -.. |database-auto| replace:: :meth:`Database.transact` -.. |transaction-class| replace:: :class:`Transaction` -.. |get-key-func| replace:: :meth:`Transaction.get_key` -.. |get-range-func| replace:: :meth:`Transaction.get_range` -.. |commit-func| replace:: :meth:`Transaction.commit` -.. |reset-func-name| replace:: :meth:`reset ` -.. |reset-func| replace:: :meth:`Transaction.reset` -.. |cancel-func| replace:: :meth:`Transaction.cancel` -.. |init-func| replace:: :func:`FDB.init` -.. |open-func| replace:: :func:`FDB.open` -.. |on-error-func| replace:: :meth:`Transaction.on_error` -.. |null-type| replace:: ``nil`` -.. |error-type| replace:: exception -.. |error-raise-type| replace:: raise -.. |future-cancel| replace:: :meth:`Future.cancel` -.. |max-watches-database-option| replace:: :meth:`Database.options.set_max_watches` -.. |future-type-string| replace:: a :class:`Future` -.. |read-your-writes-disable-option| replace:: :meth:`Transaction.options.set_read_your_writes_disable` -.. |lazy-iterator-object| replace:: :class:`Enumerator` -.. |key-meth| replace:: :meth:`Subspace.key` -.. |directory-subspace| replace:: :class:`DirectorySubspace` -.. |directory-layer| replace:: :class:`DirectoryLayer` -.. |subspace| replace:: :class:`Subspace` -.. |subspace-api| replace:: :ref:`subspaces ` -.. |as-foundationdb-key| replace:: :meth:`as_foundationdb_key` -.. |as-foundationdb-value| replace:: :meth:`as_foundationdb_value` -.. |tuple-layer| replace:: :ref:`tuple layer ` -.. |dir-path-type| replace:: an :class:`Enumerable` -.. |node-subspace| replace:: ``node_subspace`` -.. |content-subspace| replace:: ``content_subspace`` -.. |allow-manual-prefixes| replace:: ``allow_manual_prefixes`` - -.. include:: api-common.rst.inc - -##################### -Ruby API -##################### - -.. |future-type| replace:: (:ref:`future `) -.. |future-object| replace:: :ref:`Future ` object -.. |infrequent| replace:: *Infrequently used*. -.. |streaming-mode| replace:: :ref:`streaming mode ` - -Installation -============ - -The FoundationDB Ruby API is distributed in our :doc:`downloads`. - -.. note:: |separately-installed-bindings| -.. note:: |project-dependency| - -API versioning -============== - -When you require the ``FDB`` gem, it exposes only one useful method: - -.. function:: api_version(version) - - Specifies the version of the API that the application uses. This allows future versions of FoundationDB to make API changes without breaking existing programs. The current version of the API is |api-version|. - -.. note:: You must call ``FDB.api_version(...)`` before using any other part of the API. Once you have done so, the rest of the API will become available in the ``FDB`` module. - -.. note:: |api-version-rationale| - -.. warning:: |api-version-multi-version-warning| - -For API changes between version 14 and |api-version| (for the purpose of porting older programs), see :doc:`release-notes`. - -Opening a database -================== - -After requiring the ``FDB`` gem and selecting an API version, you probably want to open a :class:`Database`. The simplest way of doing this is using :func:`open`:: - - require 'fdb' - FDB.api_version 510 - db = FDB.open - -.. function:: open( cluster_file=nil, db_name="DB" ) -> Database - - |fdb-open-blurb| - - .. note:: In this release, db_name must be "DB". - - .. note:: ``fdb.open`` combines the effect of :func:`init`, :func:`create_cluster`, and :meth:`Cluster.open_database`. - -.. function:: init() -> nil - - Initializes the FoundationDB API, creating a thread for the FoundationDB client and initializing the client's networking engine. :func:`init` can only be called once. If called subsequently or after :func:`open`, it will raise a ``client_invalid_operation`` error. - -.. function:: create_cluster(cluster_file=nil) -> Cluster - - Connects to the cluster specified by :ref:`cluster_file `, or by a :ref:`default cluster file ` if ``cluster_file`` is ``nil``. - -.. global:: FDB.options - - |network-options-blurb| - - .. note:: |network-options-warning| - - .. method:: FDB.options.set_trace_enable(output_directory) -> nil - - |option-trace-enable-blurb| - - .. warning:: |option-trace-enable-warning| - - .. method:: FDB.options.set_trace_max_logs_size(bytes) -> nil - - |option-trace-max-logs-size-blurb| - - .. method:: FDB.options.set_trace_roll_size(bytes) -> nil - - |option-trace-roll-size-blurb| - - |option-disable-multi-version-client-api| - - .. method :: FDB.options.set_callbacks_on_external_threads() -> nil - - |option-callbacks-on-external-threads| - - .. method :: FDB.options.set_external_client_library(path_to_lib) -> nil - - |option-external-client-library| - - .. method :: FDB.options.set_external_client_directory(path_to_lib_directory) -> nil - - |option-external-client-directory| - - .. note:: |tls-options-burb| - - .. method :: FDB.options.set_tls_plugin(plugin_path_or_name) -> nil - - |option-tls-plugin-blurb| - - .. method :: FDB.options.set_tls_cert_path(path_to_file) -> nil - - |option-tls-cert-path-blurb| - - .. method :: FDB.options.set_tls_key_path(path_to_file) -> nil - - |option-tls-key-path-blurb| - - .. method :: FDB.options.set_tls_verify_peers(criteria) -> nil - - |option-tls-verify-peers-blurb| - - .. method :: FDB.options.set_tls_cert_bytes(bytes) -> nil - - |option-tls-cert-bytes| - - .. method :: FDB.options.set_tls_key_bytes(bytes) -> nil - - |option-tls-key-bytes| - - .. method :: FDB.options.set_disable_multi_version_client_api() -> nil - - -Cluster objects -=============== - -.. class:: Cluster - -.. method:: Cluster.open_database(name="DB") -> Database - - Opens a database with the given name. - - .. note:: In this release, name **must** be "DB". - -.. _api-ruby-keys: - -Keys and values -=============== - -|keys-values-blurb| - -|keys-values-other-types-blurb| - -``as_foundationdb_key`` and ``as_foundationdb_value`` ------------------------------------------------------ - -|as-foundationdb-blurb| - -.. warning :: |as-foundationdb-warning| - -KeyValue objects -================ - -.. class:: KeyValue - - Represents a single key-value pair in the database. This is a simple value type; mutating it won't affect your :class:`Transaction` or :class:`Database`. - - .. attr_reader:: key - - .. attr_reader:: value - -Key selectors -============= - -|keysel-blurb1| - -|keysel-blurb2| - -.. class:: KeySelector(key, or_equal, offset) - - Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods: - - .. classmethod:: last_less_than(key) -> KeySelector - - Returns a key selector referencing the last (greatest) key in the database less than the specified key. - - .. classmethod:: KeySelector.last_less_or_equal(key) -> KeySelector - - Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. - - .. classmethod:: KeySelector.first_greater_than(key) -> KeySelector - - Returns a key selector referencing the first (least) key greater than the specified key. - - .. classmethod:: KeySelector.first_greater_or_equal(key) -> KeySelector - - Returns a key selector referencing the first key greater than, or equal to, the specified key. - -.. method:: KeySelector.+(offset) -> KeySelector - - Adding an integer ``offset`` to a :class:`KeySelector` returns a new key selector referencing a key ``offset`` keys after the original :class:`KeySelector`. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. - -.. method:: KeySelector.-(offset) -> KeySelector - - Subtracting an integer ``offset`` from a :class:`KeySelector` returns a new key selector referencing a key ``offset`` keys before the original :class:`KeySelector`. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. - -Database objects -================ - -.. class:: Database - -A |database-blurb1| |database-blurb2| - -.. note:: |database-sync| - -.. method:: Database.transact() {|tr| block } - - Executes the provided block with a new transaction, commits the transaction, and retries the block as necessary in response to retryable database errors such as transaction conflicts. This is the recommended way to do operations transactionally. - - This method, along with :meth:`Transaction.transact`, makes it easy to write a transactional functions which accept either a :class:`Database` or a :class:`Transaction` as a parameter. See :ref:`transact` for explanation and examples. - - .. note:: |fdb-transactional-unknown-result-note| - -.. |sync-read| replace:: This read is fully synchronous. -.. |sync-write| replace:: This change will be committed immediately, and is fully synchronous. - -.. method:: Database.create_transaction() -> Transaction - - Starts a new transaction on the database. Consider using :meth:`Database.transact` instead, since it will automatically provide you with appropriate commit and retry behavior. - -.. method:: Database.get(key) -> String or nil - - Returns the value associated with the specified key in the database (or ``nil`` if the key does not exist). |sync-read| - -.. method:: Database.[](key) -> String or nil - - Alias of :meth:`Database.get`. - -.. method:: Database.get_key(key_selector) -> String - - Returns the key referenced by the specified :class:`KeySelector`. |sync-read| - - |database-get-key-caching-blurb| - -.. method:: Database.get_range(begin, end, options={}) -> Array - - Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an :class:`Array` of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. |sync-read| - - Each of ``begin`` and ``end`` may be a key (:class:`String` or :class:`Key`) or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. - - The ``options`` hash accepts the following optional parameters: - - ``:limit`` - Only the first ``limit`` keys (and their values) in the range will be returned. - - ``:reverse`` - If ``true``, then the keys in the range will be returned in reverse order. - - If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. - - ``:streaming_mode`` - A valid |streaming-mode|, which provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. - -.. method:: Database.get_range(begin, end, options={}) {|kv| block } -> nil - - If given a block, :meth:`Database.get_range` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. - -.. method:: Database.get_range_start_with(prefix, options={}) -> Array - - Returns all keys ``k`` such that ``k.start_with? prefix``, and their associated values, as an :class:`Array` of :class:`KeyValue` objects. The ``options`` hash accepts the same values as :meth:`Database.get_range`. |sync-read| - -.. method:: Database.get_range_start_with(prefix, options={}) {|kv| block } -> nil - - If given a block, :meth:`Database.get_range_start_with` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. - -.. method:: Database.set(key, value) -> value - - Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. Returns the same value that was passed in. |sync-write| - -.. method:: Database.[]=(key, value) -> value - - Alias of :meth:`Database.set`. - -.. method:: Database.clear(key) -> nil - - Removes the specified ``key`` (and any associated value), if it exists. |sync-write| - -.. method:: Database.clear_range(begin, end) -> nil - - Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |sync-write| - -.. method:: Database.clear_range_start_with(prefix) -> nil - - Removes all keys ``k`` such that ``k.start_with? prefix``, and their associated values. |sync-write| - -.. method:: Database.get_and_watch(key) -> [value, FutureNil] - - Returns an array ``[value, watch]``, where ``value`` is the value associated with ``key`` or ``nil`` if the key does not exist, and ``watch`` is a :class:`FutureNil` that will become ready after ``value`` changes. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.set_and_watch(key, value) -> FutureNil - - Sets ``key`` to ``value`` and returns a :class:`FutureNil` that will become ready after a subsequent change to ``value``. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.clear_and_watch(key) -> FutureNil - - Removes ``key`` (and any associated value) if it exists and returns a :class:`FutureNil` that will become ready after the value is subsequently set. - - See :meth:`Transaction.watch` for a general description of watches and their limitations. - -.. method:: Database.add(key, param) -> nil -.. method:: Database.bit_and(key, param) -> nil -.. method:: Database.bit_or(key, param) -> nil -.. method:: Database.bit_xor(key, param) -> nil - - These atomic operations behave exactly like the :ref:`associated operations ` on :class:`Transaction` objects except that the change will immediately be committed, and is fully synchronous. - - .. note :: |database-atomic-ops-idempotency-note| - -Database options ----------------- - -|database-options-blurb| - -.. method:: Database.options.set_location_cache_size(size) -> nil - - |option-location-cache-size-blurb| - -.. method:: Database.options.set_max_watches(max_watches) -> nil - - |option-max-watches-blurb| - -.. method:: Database.options.set_machine_id(id) -> nil - - |option-machine-id-blurb| - -.. method:: Database.options.set_datacenter_id(id) -> nil - - |option-datacenter-id-blurb| - -Transaction objects -=================== - -.. class:: Transaction - -A ``Transaction`` object represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a ``Transaction``. - -|transaction-blurb1| - -|transaction-blurb2| - -|transaction-blurb3| - -The most convenient way to create and use transactions is using the :meth:`Database.transact` method. - -Keys and values in FoundationDB are byte strings. FoundationDB will accept Ruby strings with any encoding, but will always return strings with ``ASCII-8BIT`` encoding (also known as ``BINARY``). To encode other data types, see the :mod:`FDB::Tuple` module and :ref:`encoding-data-types`. - -Attributes ----------- - -.. global:: Transaction.db - - |db-attribute-blurb| - -Reading data ------------- - -.. method:: Transaction.get(key) -> Value - - Returns the value associated with the specified key in the database (which may be ``nil`` if the key does not exist). - -.. method:: Transaction.[](key) - - Alias of :meth:`Transaction.get`. - -.. method:: Transaction.get_key(key_selector) -> Key - - Returns the key referenced by the specified :class:`KeySelector`. - - |transaction-get-key-caching-blurb| - -.. method:: Transaction.get_range(begin, end, options={}) -> an_enumerable - - Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an enumerable of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. - - Like a |future-object|, the returned enumerable issues asynchronous read operations to fetch data in the range, and may block while enumerating its values if the read has not completed. Data will be fetched in one more more efficient batches (depending on the value of the ``:streaming_mode`` parameter). - - Each of ``begin`` and ``end`` may be a key (:class:`String` or :class:`Key`) or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. - - The ``options`` hash accepts the following optional parameters: - - ``:limit`` - Only the first ``limit`` keys (and their values) in the range will be returned. - - ``:reverse`` - If true, then the keys in the range will be returned in reverse order. - - If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. - - ``:streaming_mode`` - A valid |streaming-mode|, which provides a hint to FoundationDB about how the returned enumerable is likely to be used. The default is ``:iterator``. - -.. method:: Transaction.get_range(begin, end, options={}) {|kv| block } -> nil - - If given a block, :meth:`Transaction.get_range` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. - -.. method:: Transaction.get_range_start_with(prefix, options={}) -> an_enumerable - - Returns all keys ``k`` such that ``k.start_with? prefix``, and their associated values, as an enumerable of :class:`KeyValue` objects (see :meth:`Transaction.get_range` for a description of the returned enumerable). - - The ``options`` hash accepts the same values as :meth:`Transaction.get_range`. - -.. method:: Transaction.get_range_start_with(prefix, options={}) {|kv| block } -> nil - - If given a block, :meth:`Transaction.get_range_start_with` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. - -Snapshot reads --------------- - -.. global:: Transaction.snapshot - - |snapshot-blurb1| - - |snapshot-blurb2| - - |snapshot-blurb3| - - |snapshot-blurb4| - -.. global:: Transaction.snapshot.db - - |db-attribute-blurb| - -.. method:: Transaction.snapshot.get(key) -> Value - - Like :meth:`Transaction.get`, but as a snapshot read. - -.. method:: Transaction.snapshot.[](key) -> Value - - Alias of :meth:`Transaction.snapshot.get`. - -.. method:: Transaction.snapshot.get_key(key_selector) -> Key - - Like :meth:`Transaction.get_key`, but as a snapshot read. - -.. method:: Transaction.snapshot.get_range(begin, end, options={}) -> an_enumerable - - Like :meth:`Transaction.get_range`, but as a snapshot read. - -.. method:: Transaction.snapshot.get_range_start_with(prefix, options={}) -> an_enumerable - - Like :meth:`Transaction.get_range_start_with`, but as a snapshot read. - -.. method:: Transaction.snapshot.get_read_version() -> Version - - Identical to :meth:`Transaction.get_read_version` (since snapshot and serializable reads use the same read version). - -Writing data ------------- - -.. |immediate-return| replace:: Returns immediately, having modified the snapshot represented by this :class:`Transaction`. - -.. method:: Transaction.set(key, value) -> nil - - Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |immediate-return| - -.. method:: Transaction.[]=(key, value) -> nil - - Alias of :meth:`Transaction.set`. - - .. note:: Although the above method returns nil, assignments in Ruby evaluate to the value assigned, so the expression ``tr[key] = value`` will return ``value``. - -.. method:: Transaction.clear(key) -> nil - - Removes the specified key (and any associated value), if it exists. |immediate-return| - -.. method:: Transaction.clear_range(begin, end) -> nil - - Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |immediate-return| - - .. note:: Unlike in the case of :meth:`Transaction.get_range`, ``begin`` and ``end`` must be keys (:class:`String` or :class:`Key`), not :class:`KeySelector`\ s. (Resolving arbitrary key selectors would prevent this method from returning immediately, introducing concurrency issues.) - -.. method:: Transaction.clear_range_start_with(prefix) -> nil - - Removes all the keys ``k`` such that ``k.start_with? prefix``, and their associated values. |immediate-return| - -.. _api-ruby-transaction-atomic-operations: - -Atomic operations ------------------ - -|atomic-ops-blurb1| - -|atomic-ops-blurb2| - -|atomic-ops-blurb3| - -.. warning :: |atomic-ops-warning| - -In each of the methods below, ``param`` should be a string appropriately packed to represent the desired value. For example:: - - # wrong - tr.add('key', 1) - - # right - tr.add('key', [1].pack('q<')) - -.. method:: Transaction.add(key, param) -> nil - - |atomic-add1| - - |atomic-add2| - -.. method:: Transaction.bit_and(key, param) -> nil - - |atomic-and| - -.. method:: Transaction.bit_or(key, param) -> nil - - |atomic-or| - -.. method:: Transaction.bit_xor(key, param) -> nil - - |atomic-xor| - -.. method:: Transaction.max(key, param) -> nil - - |atomic-max1| - - |atomic-max-min| - -.. method:: Transaction.byte_max(key, param) -> nil - - |atomic-byte-max| - -.. method:: Transaction.min(key, param) -> nil - - |atomic-min1| - - |atomic-max-min| - -.. method:: Transaction.byte_min(key, param) -> nil - - |atomic-byte-min| - -.. method:: Transaction.set_versionstamped_key(key, param) -> nil - - |atomic-set-versionstamped-key-1| - - |atomic-versionstamps-1| - - |atomic-versionstamps-2| - - |atomic-set-versionstamped-key-2| - - .. warning :: |atomic-versionstamps-tuple-warning-key| - -.. method:: Transaction.set_versionstamped_value(key, param) -> nil - - |atomic-set-versionstamped-value| - - |atomic-versionstamps-1| - - |atomic-versionstamps-2| - - .. warning :: |atomic-versionstamps-tuple-warning-value| - -Committing ----------- - -.. method:: Transaction.transact() {|tr| block } - - Yields ``self`` to the given block. - - This method, along with :meth:`Database.transact`, makes it easy to write transactional functions which accept either a :class:`Database` or a :class:`Transaction` as a parameter. See :ref:`transact` for explanation and examples. - -.. method:: Transaction.commit() -> FutureNil - - Attempt to commit the changes made in the transaction to the database. Returns a :class:`FutureNil`, representing the asynchronous result of the commit. You **must** call the :meth:`wait()` method on the returned :class:`FutureNil`, which will raise an exception if the commit failed. - - |commit-unknown-result-blurb| - - |commit-outstanding-reads-blurb| - - .. note:: Consider using :meth:`Database.transact`, which not only calls :meth:`Database.create_transaction` and :meth:`Transaction.commit` for you, but also implements the required error handling and retry logic for transactions. - - .. warning :: |used-during-commit-blurb| - -.. method:: Transaction.on_error(exception) -> FutureNil - - Determine whether an exception raised by a :class:`Transaction` method is retryable. Returns a :class:`FutureNil`. You **must** call the :meth:`wait()` method on the :class:`FutureNil`, which will return after a delay if the exception was retryable, or re-raise the exception if it was not. - - .. note:: Consider using :meth:`Database.transact`, which calls this method for you. - -.. method:: Transaction.reset() -> nil - - |transaction-reset-blurb| - -.. method:: Transaction.cancel() -> nil - - |transaction-cancel-blurb| - - .. warning :: |transaction-reset-cancel-warning| - - .. warning :: |transaction-commit-cancel-warning| - -Watches -------- - -.. method:: Transaction.watch(key) -> FutureNil - - Creates a watch and returns a :class:`FutureNil` that will become ready when the watch reports a change to the value of the specified key. - - |transaction-watch-blurb| - - |transaction-watch-committed-blurb| - - |transaction-watch-error-blurb| - - |transaction-watch-limit-blurb| - - -Conflict ranges ---------------- - -.. note:: |conflict-range-note| - -|conflict-range-blurb| - -.. method:: Transaction.add_read_conflict_range(begin, end) -> nil - - |add-read-conflict-range-blurb| - -.. method:: Transaction.add_read_conflict_key(key) -> nil - - |add-read-conflict-key-blurb| - -.. method:: Transaction.add_write_conflict_range(begin, end) -> nil - - |add-write-conflict-range-blurb| - -.. method:: Transaction.add_write_conflict_key(key) -> nil - - |add-write-conflict-key-blurb| - -Versions --------- - -Most applications should use the read version that FoundationDB determines automatically during the transaction's first read, and ignore all of these methods. - -.. method:: Transaction.set_read_version(version) -> nil - - |infrequent| Sets the database version that the transaction will read from the database. The database cannot guarantee causal consistency if this method is used (the transaction's reads will be causally consistent only if the provided read version has that property). - -.. method:: Transaction.get_read_version() -> Version - - |infrequent| Returns the transaction's read version. - -.. method:: Transaction.get_committed_version() -> Integer - - |infrequent| |transaction-get-committed-version-blurb| - -.. method:: Transaction.get_verionstamp() -> String - - |infrequent| |transaction-get-versionstamp-blurb| - -Transaction options -------------------- - -|transaction-options-blurb| - -.. _api-ruby-snapshot-ryw: - -.. method:: Transaction.options.set_snapshot_ryw_disable() -> nil - - |option-snapshot-ryw-disable-blurb| - -.. method:: Transaction.options.set_snapshot_ryw_enable() -> nil - - |option-snapshot-ryw-enable-blurb| - -.. method:: Transaction.options.set_priority_batch() -> nil - - |option-priority-batch-blurb| - -.. method:: Transaction.options.set_priority_system_immediate() -> nil - - |option-priority-system-immediate-blurb| - - .. warning:: |option-priority-system-immediate-warning| - -.. method:: Transaction.options.set_causal_read_risky() -> nil - - |option-causal-read-risky-blurb| - -.. method:: Transaction.options.set_causal_write_risky() -> nil - - |option-causal-write-risky-blurb| - -.. method:: Transaction.options.set_next_write_no_write_conflict_range() -> nil - - |option-next-write-no-write-conflict-range-blurb| - - .. note:: |option-next-write-no-write-conflict-range-note| - -.. method:: Transaction.options.set_read_your_writes_disable() -> nil - - |option-read-your-writes-disable-blurb| - - .. note:: |option-read-your-writes-disable-note| - -.. method:: Transaction.options.set_read_ahead_disable() -> nil - - |option-read-ahead-disable-blurb| - -.. method:: Transaction.options.set_access_system_keys() -> nil - - |option-access-system-keys-blurb| - - .. warning:: |option-access-system-keys-warning| - -.. method:: Transaction.options.set_read_system_keys() -> nil - - |option-read-system-keys-blurb| - - .. warning:: |option-read-system-keys-warning| - -.. method:: Transaction.options.set_retry_limit() -> nil - - |option-set-retry-limit-blurb1| - - |option-set-retry-limit-blurb2| - -.. method:: Transaction.options.set_max_retry_delay() -> nil - - |option-set-max-retry-delay-blurb| - -.. method:: Transaction.options.set_timeout() -> nil - - |option-set-timeout-blurb1| - - |option-set-timeout-blurb2| - - |option-set-timeout-blurb3| - -.. method:: Transaction.options.set_durability_dev_null_is_web_scale() -> nil - - |option-durability-dev-null-is-web-scale-blurb| - -.. _transact: - -The transact method -=================== - -When performing a database transaction, any read operation, as well as the commit itself, may fail with one of a number of errors. If the error is a retryable error, the transaction needs to be restarted from the beginning. Committing a transaction is also an asynchronous operation, and the returned :class:`FutureNil` object needs to be waited on to ensure that no errors occurred. - -The methods :meth:`Database.transact` and :meth:`Transaction.transact` are convenient wrappers that allow much of this complexity to be handled automatically. A call like :: - - db.transact do |tr| - tr['a'] = 'A' - tr['b'] = 'B' - end - -is equivalent to :: - - tr = db.create_transaction - committed = false - while !committed - begin - tr['a'] = 'A' - tr['b'] = 'B' - tr.commit.wait - committed = true - rescue FDB::Error => e - tr.on_error(e).wait - end - end - -The first form is considerably easier to read, and ensures that the transaction is correctly committed (and retried, when necessary). - -.. note:: Be careful when using control flow constructs within the block passed to :meth:`transact`. ``return`` or ``break`` will exit the retry loop *without committing* the transaction. Use ``next`` to exit the block and commit the transaction. - -The :meth:`Transaction.transact` method, which logically does nothing, makes it easy to write functions that operate on either a :class:`Database` or :class:`Transaction`. Consider the following method:: - - def increment(db_or_tr, key) - db_or_tr.transact do |tr| - tr[key] = (tr[key].to_i + 1).to_s - end - end - -This method can be called with a :class:`Database`, and it will do its job atomically:: - - increment(db, 'number') - -It can also be called by another transactional method with a transaction:: - - def increment_both(db_or_tr, key1, key2) - db_or_tr.transact do |tr| - increment(tr, key1) - increment(tr, key2) - end - end - -In the second case, ``increment`` will use provided transaction and will not commit it or retry errors, since that is the responsibility of its caller, ``increment_both``. - -.. note:: |fdb-transactional-unknown-result-note| - -.. _api-ruby-future: - -Future objects -============== - -|future-blurb1| - -.. _api-ruby-block: - -When a future object "blocks", the ruby thread is blocked, but the global interpreter lock is released. - -When used in a conditional expression, a future object will evaluate to true, even if its value is nil. To test for nil, you must explicitly use the nil?() method:: - - if tr['a'].nil? - -All future objects are a subclass of the :class:`Future` type. - -.. class:: Future - - .. method:: Future.ready?() -> bool - - Immediately returns true if the future object is ready, false otherwise. - - .. method:: Future.block_until_ready() -> nil - - Blocks until the future object is ready. - - .. method:: Future.on_ready() {|future| block } -> nil - - Yields ``self`` to the given block when the future object is ready. If the future object is ready at the time :meth:`on_ready` is called, the block may be called immediately in the current thread (although this behavior is not guaranteed). Otherwise, the call may be delayed and take place on the thread with which the client was initialized. Therefore, the block is responsible for any needed thread synchronization (and/or for posting work to your application's event loop, thread pool, etc., as may be required by your application's architecture). - - .. warning:: |fdb-careful-with-callbacks-blurb| - - .. method:: Future.cancel() -> nil - - |future-cancel-blurb| - - .. classmethod:: Future.wait_for_any(*futures) -> Fixnum - - Does not return until at least one of the given future objects is ready. Returns the index in the parameter list of a ready future object. - -Asynchronous methods return one of the following subclasses of :class:`Future`: - -.. class:: Value - -.. class:: Key - - Both types are future :class:`String` objects. Objects of these types respond to the same methods as objects of type :class:`String`, and may be passed to any method that expects a :class:`String`. - - An implementation quirk of :class:`Value` is that it will never evaluate to ``false``, even if its value is ``nil``. It is important to use ``if value.nil?`` rather than ``if ~value`` when checking to see if a key was not present in the database. - -.. class:: Version - - This type is a future :class:`Integer` object. Objects of this type respond to the same methods as objects of type :class:`Integer`, and may be passed to any method that expects a :class:`Integer`. - -.. class:: FutureArray - - This type is a future :class:`Array` object. Objects of this type respond to the same methods as objects of type :class:`Array`, and may be passed to any method that expects a :class:`Array`. - -.. class:: FutureNil - - This type is a future returned from asynchronous methods that logically have no return value. - - .. method:: FutureNil.wait() -> nil - - For a :class:`FutureNil` object returned by :meth:`Transaction.commit` or :meth:`Transaction.on_error`, you must call :meth:`FutureNil.wait`, which will return ``nil`` if the operation succeeds or raise an :exc:`FDB::Error` if an error occurred. Failure to call :meth:`FutureNil.wait` on a returned :class:`FutureNil` object means that any potential errors raised by the asynchronous operation that returned the object *will not be seen*, and represents a significant error in your code. - -.. _ruby streaming mode: - -Streaming modes -=============== - -|streaming-mode-blurb1| - -|streaming-mode-blurb2| - -The following streaming modes are available: - -.. const:: :iterator - - *The default.* The client doesn't know how much of the range it is likely to used and wants different performance concerns to be balanced. - - Only a small portion of data is transferred to the client initially (in order to minimize costs if the client doesn't read the entire range), and as the caller iterates over more items in the range larger batches will be transferred in order to maximize throughput. - -.. const:: :want_all - - The client intends to consume the entire range and would like it all transferred as early as possible. - -.. const:: :small - - |infrequent| Transfer data in batches small enough to not be much more expensive than reading individual rows, to minimize cost if iteration stops early. - -.. const:: :medium - - |infrequent| Transfer data in batches sized in between ``:small`` and ``:large``. - -.. const:: :large - - |infrequent| Transfer data in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the client stops iteration early, some disk and network bandwidth may be wasted. The batch size may still be too small to allow a single client to get high throughput from the database, so if that is what you need consider ``:serial``. - -.. const:: :serial - - Transfer data in batches large enough that an individual client can get reasonable read bandwidth from the database. If the client stops iteration early, considerable disk and network bandwidth may be wasted. - -.. const:: :exact - - |infrequent| The client has passed a specific row limit and wants that many rows delivered in a single batch. This is not particularly useful in Ruby because enumerable functionality makes batches of data transparent, so use ``:want_all`` instead. - -Errors -====== - -Errors in the FoundationDB API are raised as exceptions of type :class:`FDB::Error`. These errors may be displayed for diagnostic purposes, but generally should be passed to :meth:`Transaction.on_error`. When using :meth:`Database.transact`, appropriate errors will be retried automatically. - -.. class:: Error - - .. attr_reader:: code - - A :class:`Fixnum` associated with the error type. - - .. method:: description() -> String - - Returns a somewhat human-readable description of the error. - -.. warning:: You should only use the :attr:`code` attribute for programmatic comparisons, as the description of the error may change at any time. Whenever possible, use the :meth:`Transaction.on_error` method to handle :class:`FDB::Error` exceptions. - -.. _api-ruby-tuple-layer: - -Tuple layer -=========== - -.. module:: FDB::Tuple - -|tuple-layer-blurb| - -.. note:: |tuple-layer-note| - -In the FoundationDB Ruby API, a tuple is an :class:`Enumerable` of elements of the following data types: - -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Type | Legal Values | Canonical Value | -+======================+=============================================================================+==============================================================================+ -| Null value | ``nil`` | ``nil`` | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Byte string | Any value ``v`` where ``v.kind_of? String == true`` and ``v.encoding`` is | ``String`` with encoding ``Encoding::ASCII_8BIT`` | -| | either ``Encoding::ASCII_8BIT`` (aka ``Encoding::BINARY``) or | | -| | ``Encoding::US_ASCII`` (aka ``Encoding::ASCII``) | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Unicode string | Any value ``v`` where ``v.kind_of? String == true`` and ``v.encoding`` is | ``String`` with encoding ``Encoding::UTF_8`` | -| | ``Encoding::UTF_8`` | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| 64-bit signed integer| Any value ``v`` where ``v.kind_of? Integer == true`` and ``-2**64+1 <= v <= | ``Fixnum`` or ``Bignum`` (depending on the magnitude of the value) | -| | 2**64-1`` | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Floating point number| Any value ``v`` where ``v.kind_of? FDB::Tuple::SingleFloat`` where | :class:`FDB::Tuple::SingleFloat` | -| (single-precision) | ``v.value.kind_of? Float`` and ``v.value`` fits inside an IEEE 754 32-bit | | -| | floating-point number. | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Floating point number| Any value ``v`` where ``v.kind_of? Float`` | ``Float`` | -| (double-precision) | | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Boolean | Any value ``v`` where ``v.kind_of? Boolean`` | ``Boolean`` | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| UUID | Any value ``v`` where ``v.kind_of? FDB::Tuple::UUID`` where | :class:`FDB::Tuple::UUID` | -| | ``v.data.kind_of? String`` and ``v.data.encoding`` is ``Encoding::BINARY`` | | -| | and ``v.data.length == 16`` | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ -| Array | Any value ``v`` such that ``v.kind_of? Array`` and each element within | ``Array`` | -| | ``v`` is one of the supported types with a legal value. | | -+----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ - -Note that as Ruby does not have native support for single-precision floating point values and UUIDs, tuple elements -of those types are returned instead as :class:`FDB::Tuple::SingleFloat` and :class:`FDB::Tuple::UUID` instances. -These are simple classes that just wrap their underlying values, and they are not intended to offer all of the methods -that a more fully-featured library for handling floating point values or UUIDs might offer. Most applications -should use their library of choice for handling these values and then convert to the appropriate tuple-type -when serializing for storage into the key-value store. - -A single tuple element is ordered first by its type, and then by its value. - -If ``T`` is an :class:`Enumerable` meeting these criteria, then conceptually:: - - T == FDB::Tuple.unpack(FDB::Tuple.pack(T)) - -.. note:: Unpacking a tuple always returns an :class:`Array` of elements in a canonical representation, so packing and then unpacking a tuple may result in an equivalent but not identical representation. - -.. function:: pack(tuple) -> String - - Returns a key encoding the given tuple. - -.. function:: unpack(key) -> Array - - Returns the tuple encoded by the given key. Each element in the :class:`Array` will either be ``nil``, a :class:`String` (with encoding ``Encoding::ASCII_8BIT`` for byte strings or ``Encoding::UTF_8`` for unicode strings), or a :class:`Fixnum` or :class:`Bignum` for integers, depending on the magnitude. - -.. function:: range(tuple) -> Array - - Returns the range containing all keys that encode tuples strictly starting with ``tuple`` (that is, all tuples of greater length than tuple of which tuple is a prefix). - - The range will be returned as an :class:`Array` of two elements, and may be used with any FoundationDB methods that require a range:: - - r = FDB::Tuple.range(T) - tr.get_range(r[0], r[1]) { |kv| ... } - -.. class:: FDB::Tuple::SingleFloat(value) - - Wrapper around a single-precision floating point value. The ``value`` parameter should be a ``Float`` that - can be encoded as an IEEE 754 floating point number. If the float does not fit within a IEEE 754 floating point - integer, there may be a loss of precision. - -.. attr_reader:: FDB::Tuple::SingleFloat.value - - The underlying value of the ``SingleFloat`` object. This should have type ``Float``. - -.. function:: FDB::Tuple::SingleFloat.<=> -> Fixnum - - Comparison method for ``SingleFloat`` objects. This will compare the values based on their float value. This - will sort the values in a manner consistent with the way items are sorted as keys in the database, which can - be different from standard float comparison in that -0.0 is considered to be strictly less than 0.0 and NaN - values are sorted based on their byte representation rather than being considered incomparable. - -.. function:: FDB::Tuple::SingleFloat.to_s -> String - - Creates a string representation of the ``SingleFloat``. This will just return the string representation of the underlying - value. - -.. class:: FDB::Tuple::UUID(data) - - Wrapper around a 128-bit UUID. The ``data`` parameter should be a byte string of length 16 and is taken to - be the big-endian byte representation of the UUID. If ``data`` is not of length 16, an |error-type| is thrown. - -.. attr_reader:: FDB::Tuple::UUID.data - - The UUID data as a byte array of length 16. This is stored in big-endian order. - -.. method:: FDB::Tuple::UUID.<=> -> Fixnum - - Comparison method for ``UUID`` objects. It will compare the UUID representations as unsigned byte arrays. - This is the same order the database uses when comparing serialized UUIDs when they are used as part - of a key. - -.. method:: FDB::Tuple::UUID.to_s -> String - - Creates a string representation of the ``UUID``. This will be a string of length 32 containing the hex representation - of the UUID bytes. - -.. _api-ruby-subspaces: - -Subspaces -========= - -.. currentmodule:: FDB - -|subspace-blurb1| - -|subspace-blurb2| - -.. note:: For general guidance on subspace usage, see the discussion in the :ref:`Developer Guide `. - -.. class:: Subspace(prefix_tuple=[], raw_prefix='') - - |subspace-blurb3| - -.. method:: Subspace.key() -> String - - |subspace-key-blurb| - -.. method:: Subspace.pack(tuple) -> String - - |subspace-pack-blurb| - -.. method:: Subspace.unpack(key) -> Array - - |subspace-unpack-blurb| - -.. method:: Subspace.range(tuple=[]) -> Array - - |subspace-range-blurb| - - The range will be returned as an :class:`Array` of two elements, and may be used with any FoundationDB methods that require a range:: - - r = subspace.range(['A', 2]) - tr.get_range(r[0], r[1]) { |kv| ... } - -.. method:: Subspace.contains?(key) -> bool - - |subspace-contains-blurb| - -.. method:: Subspace.as_foundationdb_key() -> String - - |subspace-as-foundationdb-key-blurb| This method serves to support the :ref:`as_foundationdb_key() ` convenience interface. - -.. method:: Subspace.subspace(tuple) -> Subspace - - |subspace-subspace-blurb| - -.. method:: Subspace.[](item) -> Subspace - - Shorthand for Subspace.subspace([item]). This function can be combined with the :meth:`Subspace.as_foundationdb_key()` convenience to turn this:: - - s = FDB::Subspace.new(['x']) - tr[s.pack(['foo', 'bar', 1])] = '' - - into this:: - - s = FDB::Subspace.new(['x']) - tr[s['foo']['bar'][1]] = '' - -.. _api-ruby-directories: - -Directories -=========== - -|directory-blurb1| - -.. note:: For general guidance on directory usage, see the discussion in the :ref:`Developer Guide `. - -|directory-blurb2| - -|directory-blurb3| - -.. global:: FDB::directory - - The default instance of :class:`DirectoryLayer`. - -.. class:: DirectoryLayer(node_subspace=Subspace.new([], "\\xFE"), content_subspace=Subspace.new, allow_manual_prefixes=false) - - |directory-layer-blurb| - -.. method:: DirectoryLayer.create_or_open(db_or_tr, path, options={}) -> DirectorySubspace - - |directory-create-or-open-blurb| - - If the byte string ``:layer`` is specified in ``options`` and the directory is new, it is recorded as the layer; if ``:layer`` is specified and the directory already exists, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.open(db_or_tr, path, options={}) -> DirectorySubspace - - |directory-open-blurb| - - If the byte string ``:layer`` is specified in ``options``, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.create(db_or_tr, path, options={}) -> DirectorySubspace - - |directory-create-blurb| - - If the byte string ``:prefix`` is specified in ``options``, the directory is created with the given physical prefix; otherwise a prefix is allocated automatically. - - If the byte string ``:layer`` is specified in ``options``, it is recorded with the directory and will be checked by future calls to open. - - |directory-create-or-open-return-blurb| - -.. method:: DirectoryLayer.move(db_or_tr, old_path, new_path) -> DirectorySubspace - - |directory-move-blurb| - - |directory-move-return-blurb| - -.. method:: DirectoryLayer.remove(db_or_tr, path) -> bool - - |directory-remove-blurb| - - .. warning:: |directory-remove-warning| - -.. method:: DirectoryLayer.remove_if_exists(db_or_tr, path) -> bool - - |directory-remove-if-exists-blurb| - - .. warning:: |directory-remove-warning| - -.. method:: DirectoryLayer.list(db_or_tr, path=[]) -> Enumerable - - Returns an :class:`Enumerable` of names of the immediate subdirectories of the directory at ``path``. Each name is a unicode string representing the last component of a subdirectory's path. - -.. method:: DirectoryLayer.exists?(db_or_tr, path) -> bool - - |directory-exists-blurb| - -.. method:: DirectoryLayer.layer() -> String - - |directory-get-layer-blurb| - -.. method:: DirectoryLayer.path() -> Enumerable - - |directory-get-path-blurb| - -.. _api-ruby-directory-subspace: - -DirectorySubspace ------------------ - -.. class:: DirectorySubspace - - |directory-subspace-blurb| - -.. method:: DirectorySubspace.move_to(db_or_tr, new_absolute_path) -> DirectorySubspace - - |directory-move-to-blurb| - - |directory-move-return-blurb| - -Locality information -==================== - -.. module:: FDB::Locality - -|locality-api-blurb| - -.. function:: get_boundary_keys(db_or_tr, begin, end) -> Enumerator - - |locality-get-boundary-keys-blurb| - - |locality-get-boundary-keys-db-or-tr| - - Like a |future-object|, the returned :class:`Enumerator` issues asynchronous read operations to fetch data in the range, and may block while enumerating its values if the read has not completed. - - |locality-get-boundary-keys-warning-danger| - -.. function:: get_addresses_for_key(tr, key) -> Array - - |locality-get-addresses-for-key-blurb| +.. default-domain:: rb +.. highlight:: ruby +.. module:: FDB + +.. |database-type| replace:: ``Database`` +.. |database-class| replace:: :class:`Database` +.. |database-auto| replace:: :meth:`Database.transact` +.. |transaction-class| replace:: :class:`Transaction` +.. |get-key-func| replace:: :meth:`Transaction.get_key` +.. |get-range-func| replace:: :meth:`Transaction.get_range` +.. |commit-func| replace:: :meth:`Transaction.commit` +.. |reset-func-name| replace:: :meth:`reset ` +.. |reset-func| replace:: :meth:`Transaction.reset` +.. |cancel-func| replace:: :meth:`Transaction.cancel` +.. |init-func| replace:: :func:`FDB.init` +.. |open-func| replace:: :func:`FDB.open` +.. |on-error-func| replace:: :meth:`Transaction.on_error` +.. |null-type| replace:: ``nil`` +.. |error-type| replace:: exception +.. |error-raise-type| replace:: raise +.. |future-cancel| replace:: :meth:`Future.cancel` +.. |max-watches-database-option| replace:: :meth:`Database.options.set_max_watches` +.. |future-type-string| replace:: a :class:`Future` +.. |read-your-writes-disable-option| replace:: :meth:`Transaction.options.set_read_your_writes_disable` +.. |lazy-iterator-object| replace:: :class:`Enumerator` +.. |key-meth| replace:: :meth:`Subspace.key` +.. |directory-subspace| replace:: :class:`DirectorySubspace` +.. |directory-layer| replace:: :class:`DirectoryLayer` +.. |subspace| replace:: :class:`Subspace` +.. |subspace-api| replace:: :ref:`subspaces ` +.. |as-foundationdb-key| replace:: :meth:`as_foundationdb_key` +.. |as-foundationdb-value| replace:: :meth:`as_foundationdb_value` +.. |tuple-layer| replace:: :ref:`tuple layer ` +.. |dir-path-type| replace:: an :class:`Enumerable` +.. |node-subspace| replace:: ``node_subspace`` +.. |content-subspace| replace:: ``content_subspace`` +.. |allow-manual-prefixes| replace:: ``allow_manual_prefixes`` + +.. include:: api-common.rst.inc + +##################### +Ruby API +##################### + +.. |future-type| replace:: (:ref:`future `) +.. |future-object| replace:: :ref:`Future ` object +.. |infrequent| replace:: *Infrequently used*. +.. |streaming-mode| replace:: :ref:`streaming mode ` + +Installation +============ + +The FoundationDB Ruby API is distributed in our :doc:`downloads`. + +.. note:: |separately-installed-bindings| +.. note:: |project-dependency| + +API versioning +============== + +When you require the ``FDB`` gem, it exposes only one useful method: + +.. function:: api_version(version) + + Specifies the version of the API that the application uses. This allows future versions of FoundationDB to make API changes without breaking existing programs. The current version of the API is |api-version|. + +.. note:: You must call ``FDB.api_version(...)`` before using any other part of the API. Once you have done so, the rest of the API will become available in the ``FDB`` module. + +.. note:: |api-version-rationale| + +.. warning:: |api-version-multi-version-warning| + +For API changes between version 14 and |api-version| (for the purpose of porting older programs), see :doc:`release-notes`. + +Opening a database +================== + +After requiring the ``FDB`` gem and selecting an API version, you probably want to open a :class:`Database`. The simplest way of doing this is using :func:`open`:: + + require 'fdb' + FDB.api_version 520 + db = FDB.open + +.. function:: open( cluster_file=nil, db_name="DB" ) -> Database + + |fdb-open-blurb| + + .. note:: In this release, db_name must be "DB". + + .. note:: ``fdb.open`` combines the effect of :func:`init`, :func:`create_cluster`, and :meth:`Cluster.open_database`. + +.. function:: init() -> nil + + Initializes the FoundationDB API, creating a thread for the FoundationDB client and initializing the client's networking engine. :func:`init` can only be called once. If called subsequently or after :func:`open`, it will raise a ``client_invalid_operation`` error. + +.. function:: create_cluster(cluster_file=nil) -> Cluster + + Connects to the cluster specified by :ref:`cluster_file `, or by a :ref:`default cluster file ` if ``cluster_file`` is ``nil``. + +.. global:: FDB.options + + |network-options-blurb| + + .. note:: |network-options-warning| + + .. method:: FDB.options.set_trace_enable(output_directory) -> nil + + |option-trace-enable-blurb| + + .. warning:: |option-trace-enable-warning| + + .. method:: FDB.options.set_trace_max_logs_size(bytes) -> nil + + |option-trace-max-logs-size-blurb| + + .. method:: FDB.options.set_trace_roll_size(bytes) -> nil + + |option-trace-roll-size-blurb| + + |option-disable-multi-version-client-api| + + .. method :: FDB.options.set_callbacks_on_external_threads() -> nil + + |option-callbacks-on-external-threads| + + .. method :: FDB.options.set_external_client_library(path_to_lib) -> nil + + |option-external-client-library| + + .. method :: FDB.options.set_external_client_directory(path_to_lib_directory) -> nil + + |option-external-client-directory| + + .. note:: |tls-options-burb| + + .. method :: FDB.options.set_tls_plugin(plugin_path_or_name) -> nil + + |option-tls-plugin-blurb| + + .. method :: FDB.options.set_tls_cert_path(path_to_file) -> nil + + |option-tls-cert-path-blurb| + + .. method :: FDB.options.set_tls_key_path(path_to_file) -> nil + + |option-tls-key-path-blurb| + + .. method :: FDB.options.set_tls_verify_peers(criteria) -> nil + + |option-tls-verify-peers-blurb| + + .. method :: FDB.options.set_tls_cert_bytes(bytes) -> nil + + |option-tls-cert-bytes| + + .. method :: FDB.options.set_tls_key_bytes(bytes) -> nil + + |option-tls-key-bytes| + + .. method :: FDB.options.set_disable_multi_version_client_api() -> nil + + +Cluster objects +=============== + +.. class:: Cluster + +.. method:: Cluster.open_database(name="DB") -> Database + + Opens a database with the given name. + + .. note:: In this release, name **must** be "DB". + +.. _api-ruby-keys: + +Keys and values +=============== + +|keys-values-blurb| + +|keys-values-other-types-blurb| + +``as_foundationdb_key`` and ``as_foundationdb_value`` +----------------------------------------------------- + +|as-foundationdb-blurb| + +.. warning :: |as-foundationdb-warning| + +KeyValue objects +================ + +.. class:: KeyValue + + Represents a single key-value pair in the database. This is a simple value type; mutating it won't affect your :class:`Transaction` or :class:`Database`. + + .. attr_reader:: key + + .. attr_reader:: value + +Key selectors +============= + +|keysel-blurb1| + +|keysel-blurb2| + +.. class:: KeySelector(key, or_equal, offset) + + Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods: + + .. classmethod:: last_less_than(key) -> KeySelector + + Returns a key selector referencing the last (greatest) key in the database less than the specified key. + + .. classmethod:: KeySelector.last_less_or_equal(key) -> KeySelector + + Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. + + .. classmethod:: KeySelector.first_greater_than(key) -> KeySelector + + Returns a key selector referencing the first (least) key greater than the specified key. + + .. classmethod:: KeySelector.first_greater_or_equal(key) -> KeySelector + + Returns a key selector referencing the first key greater than, or equal to, the specified key. + +.. method:: KeySelector.+(offset) -> KeySelector + + Adding an integer ``offset`` to a :class:`KeySelector` returns a new key selector referencing a key ``offset`` keys after the original :class:`KeySelector`. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. + +.. method:: KeySelector.-(offset) -> KeySelector + + Subtracting an integer ``offset`` from a :class:`KeySelector` returns a new key selector referencing a key ``offset`` keys before the original :class:`KeySelector`. FoundationDB does not efficiently resolve key selectors with large offsets, so :ref:`dont-use-key-selectors-for-paging`. + +Database objects +================ + +.. class:: Database + +A |database-blurb1| |database-blurb2| + +.. note:: |database-sync| + +.. method:: Database.transact() {|tr| block } + + Executes the provided block with a new transaction, commits the transaction, and retries the block as necessary in response to retryable database errors such as transaction conflicts. This is the recommended way to do operations transactionally. + + This method, along with :meth:`Transaction.transact`, makes it easy to write a transactional functions which accept either a :class:`Database` or a :class:`Transaction` as a parameter. See :ref:`transact` for explanation and examples. + + .. note:: |fdb-transactional-unknown-result-note| + +.. |sync-read| replace:: This read is fully synchronous. +.. |sync-write| replace:: This change will be committed immediately, and is fully synchronous. + +.. method:: Database.create_transaction() -> Transaction + + Starts a new transaction on the database. Consider using :meth:`Database.transact` instead, since it will automatically provide you with appropriate commit and retry behavior. + +.. method:: Database.get(key) -> String or nil + + Returns the value associated with the specified key in the database (or ``nil`` if the key does not exist). |sync-read| + +.. method:: Database.[](key) -> String or nil + + Alias of :meth:`Database.get`. + +.. method:: Database.get_key(key_selector) -> String + + Returns the key referenced by the specified :class:`KeySelector`. |sync-read| + + |database-get-key-caching-blurb| + +.. method:: Database.get_range(begin, end, options={}) -> Array + + Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an :class:`Array` of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. |sync-read| + + Each of ``begin`` and ``end`` may be a key (:class:`String` or :class:`Key`) or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. + + The ``options`` hash accepts the following optional parameters: + + ``:limit`` + Only the first ``limit`` keys (and their values) in the range will be returned. + + ``:reverse`` + If ``true``, then the keys in the range will be returned in reverse order. + + If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. + + ``:streaming_mode`` + A valid |streaming-mode|, which provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. + +.. method:: Database.get_range(begin, end, options={}) {|kv| block } -> nil + + If given a block, :meth:`Database.get_range` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. + +.. method:: Database.get_range_start_with(prefix, options={}) -> Array + + Returns all keys ``k`` such that ``k.start_with? prefix``, and their associated values, as an :class:`Array` of :class:`KeyValue` objects. The ``options`` hash accepts the same values as :meth:`Database.get_range`. |sync-read| + +.. method:: Database.get_range_start_with(prefix, options={}) {|kv| block } -> nil + + If given a block, :meth:`Database.get_range_start_with` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. + +.. method:: Database.set(key, value) -> value + + Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. Returns the same value that was passed in. |sync-write| + +.. method:: Database.[]=(key, value) -> value + + Alias of :meth:`Database.set`. + +.. method:: Database.clear(key) -> nil + + Removes the specified ``key`` (and any associated value), if it exists. |sync-write| + +.. method:: Database.clear_range(begin, end) -> nil + + Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |sync-write| + +.. method:: Database.clear_range_start_with(prefix) -> nil + + Removes all keys ``k`` such that ``k.start_with? prefix``, and their associated values. |sync-write| + +.. method:: Database.get_and_watch(key) -> [value, FutureNil] + + Returns an array ``[value, watch]``, where ``value`` is the value associated with ``key`` or ``nil`` if the key does not exist, and ``watch`` is a :class:`FutureNil` that will become ready after ``value`` changes. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.set_and_watch(key, value) -> FutureNil + + Sets ``key`` to ``value`` and returns a :class:`FutureNil` that will become ready after a subsequent change to ``value``. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.clear_and_watch(key) -> FutureNil + + Removes ``key`` (and any associated value) if it exists and returns a :class:`FutureNil` that will become ready after the value is subsequently set. + + See :meth:`Transaction.watch` for a general description of watches and their limitations. + +.. method:: Database.add(key, param) -> nil +.. method:: Database.bit_and(key, param) -> nil +.. method:: Database.bit_or(key, param) -> nil +.. method:: Database.bit_xor(key, param) -> nil + + These atomic operations behave exactly like the :ref:`associated operations ` on :class:`Transaction` objects except that the change will immediately be committed, and is fully synchronous. + + .. note :: |database-atomic-ops-idempotency-note| + +Database options +---------------- + +|database-options-blurb| + +.. method:: Database.options.set_location_cache_size(size) -> nil + + |option-location-cache-size-blurb| + +.. method:: Database.options.set_max_watches(max_watches) -> nil + + |option-max-watches-blurb| + +.. method:: Database.options.set_machine_id(id) -> nil + + |option-machine-id-blurb| + +.. method:: Database.options.set_datacenter_id(id) -> nil + + |option-datacenter-id-blurb| + +Transaction objects +=================== + +.. class:: Transaction + +A ``Transaction`` object represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a ``Transaction``. + +|transaction-blurb1| + +|transaction-blurb2| + +|transaction-blurb3| + +The most convenient way to create and use transactions is using the :meth:`Database.transact` method. + +Keys and values in FoundationDB are byte strings. FoundationDB will accept Ruby strings with any encoding, but will always return strings with ``ASCII-8BIT`` encoding (also known as ``BINARY``). To encode other data types, see the :mod:`FDB::Tuple` module and :ref:`encoding-data-types`. + +Attributes +---------- + +.. global:: Transaction.db + + |db-attribute-blurb| + +Reading data +------------ + +.. method:: Transaction.get(key) -> Value + + Returns the value associated with the specified key in the database (which may be ``nil`` if the key does not exist). + +.. method:: Transaction.[](key) + + Alias of :meth:`Transaction.get`. + +.. method:: Transaction.get_key(key_selector) -> Key + + Returns the key referenced by the specified :class:`KeySelector`. + + |transaction-get-key-caching-blurb| + +.. method:: Transaction.get_range(begin, end, options={}) -> an_enumerable + + Returns all keys ``k`` such that ``begin <= k < end`` and their associated values as an enumerable of :class:`KeyValue` objects. Note the exclusion of ``end`` from the range. + + Like a |future-object|, the returned enumerable issues asynchronous read operations to fetch data in the range, and may block while enumerating its values if the read has not completed. Data will be fetched in one more more efficient batches (depending on the value of the ``:streaming_mode`` parameter). + + Each of ``begin`` and ``end`` may be a key (:class:`String` or :class:`Key`) or a :class:`KeySelector`. Note that in the case of a :class:`KeySelector`, the exclusion of ``end`` from the range still applies. + + The ``options`` hash accepts the following optional parameters: + + ``:limit`` + Only the first ``limit`` keys (and their values) in the range will be returned. + + ``:reverse`` + If true, then the keys in the range will be returned in reverse order. + + If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. + + ``:streaming_mode`` + A valid |streaming-mode|, which provides a hint to FoundationDB about how the returned enumerable is likely to be used. The default is ``:iterator``. + +.. method:: Transaction.get_range(begin, end, options={}) {|kv| block } -> nil + + If given a block, :meth:`Transaction.get_range` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. + +.. method:: Transaction.get_range_start_with(prefix, options={}) -> an_enumerable + + Returns all keys ``k`` such that ``k.start_with? prefix``, and their associated values, as an enumerable of :class:`KeyValue` objects (see :meth:`Transaction.get_range` for a description of the returned enumerable). + + The ``options`` hash accepts the same values as :meth:`Transaction.get_range`. + +.. method:: Transaction.get_range_start_with(prefix, options={}) {|kv| block } -> nil + + If given a block, :meth:`Transaction.get_range_start_with` yields each :class:`KeyValue` in the range that would otherwise have been returned to ``block``. + +Snapshot reads +-------------- + +.. global:: Transaction.snapshot + + |snapshot-blurb1| + + |snapshot-blurb2| + + |snapshot-blurb3| + + |snapshot-blurb4| + +.. global:: Transaction.snapshot.db + + |db-attribute-blurb| + +.. method:: Transaction.snapshot.get(key) -> Value + + Like :meth:`Transaction.get`, but as a snapshot read. + +.. method:: Transaction.snapshot.[](key) -> Value + + Alias of :meth:`Transaction.snapshot.get`. + +.. method:: Transaction.snapshot.get_key(key_selector) -> Key + + Like :meth:`Transaction.get_key`, but as a snapshot read. + +.. method:: Transaction.snapshot.get_range(begin, end, options={}) -> an_enumerable + + Like :meth:`Transaction.get_range`, but as a snapshot read. + +.. method:: Transaction.snapshot.get_range_start_with(prefix, options={}) -> an_enumerable + + Like :meth:`Transaction.get_range_start_with`, but as a snapshot read. + +.. method:: Transaction.snapshot.get_read_version() -> Version + + Identical to :meth:`Transaction.get_read_version` (since snapshot and serializable reads use the same read version). + +Writing data +------------ + +.. |immediate-return| replace:: Returns immediately, having modified the snapshot represented by this :class:`Transaction`. + +.. method:: Transaction.set(key, value) -> nil + + Associates the given ``key`` and ``value``. Overwrites any prior value associated with ``key``. |immediate-return| + +.. method:: Transaction.[]=(key, value) -> nil + + Alias of :meth:`Transaction.set`. + + .. note:: Although the above method returns nil, assignments in Ruby evaluate to the value assigned, so the expression ``tr[key] = value`` will return ``value``. + +.. method:: Transaction.clear(key) -> nil + + Removes the specified key (and any associated value), if it exists. |immediate-return| + +.. method:: Transaction.clear_range(begin, end) -> nil + + Removes all keys ``k`` such that ``begin <= k < end``, and their associated values. |immediate-return| + + .. note:: Unlike in the case of :meth:`Transaction.get_range`, ``begin`` and ``end`` must be keys (:class:`String` or :class:`Key`), not :class:`KeySelector`\ s. (Resolving arbitrary key selectors would prevent this method from returning immediately, introducing concurrency issues.) + +.. method:: Transaction.clear_range_start_with(prefix) -> nil + + Removes all the keys ``k`` such that ``k.start_with? prefix``, and their associated values. |immediate-return| + +.. _api-ruby-transaction-atomic-operations: + +Atomic operations +----------------- + +|atomic-ops-blurb1| + +|atomic-ops-blurb2| + +|atomic-ops-blurb3| + +.. warning :: |atomic-ops-warning| + +In each of the methods below, ``param`` should be a string appropriately packed to represent the desired value. For example:: + + # wrong + tr.add('key', 1) + + # right + tr.add('key', [1].pack('q<')) + +.. method:: Transaction.add(key, param) -> nil + + |atomic-add1| + + |atomic-add2| + +.. method:: Transaction.bit_and(key, param) -> nil + + |atomic-and| + +.. method:: Transaction.bit_or(key, param) -> nil + + |atomic-or| + +.. method:: Transaction.bit_xor(key, param) -> nil + + |atomic-xor| + +.. method:: Transaction.max(key, param) -> nil + + |atomic-max1| + + |atomic-max-min| + +.. method:: Transaction.byte_max(key, param) -> nil + + |atomic-byte-max| + +.. method:: Transaction.min(key, param) -> nil + + |atomic-min1| + + |atomic-max-min| + +.. method:: Transaction.byte_min(key, param) -> nil + + |atomic-byte-min| + +.. method:: Transaction.set_versionstamped_key(key, param) -> nil + + |atomic-set-versionstamped-key-1| + + |atomic-versionstamps-1| + + |atomic-versionstamps-2| + + |atomic-set-versionstamped-key-2| + + .. warning :: |atomic-versionstamps-tuple-warning-key| + +.. method:: Transaction.set_versionstamped_value(key, param) -> nil + + |atomic-set-versionstamped-value| + + |atomic-versionstamps-1| + + |atomic-versionstamps-2| + + .. warning :: |atomic-versionstamps-tuple-warning-value| + +Committing +---------- + +.. method:: Transaction.transact() {|tr| block } + + Yields ``self`` to the given block. + + This method, along with :meth:`Database.transact`, makes it easy to write transactional functions which accept either a :class:`Database` or a :class:`Transaction` as a parameter. See :ref:`transact` for explanation and examples. + +.. method:: Transaction.commit() -> FutureNil + + Attempt to commit the changes made in the transaction to the database. Returns a :class:`FutureNil`, representing the asynchronous result of the commit. You **must** call the :meth:`wait()` method on the returned :class:`FutureNil`, which will raise an exception if the commit failed. + + |commit-unknown-result-blurb| + + |commit-outstanding-reads-blurb| + + .. note:: Consider using :meth:`Database.transact`, which not only calls :meth:`Database.create_transaction` and :meth:`Transaction.commit` for you, but also implements the required error handling and retry logic for transactions. + + .. warning :: |used-during-commit-blurb| + +.. method:: Transaction.on_error(exception) -> FutureNil + + Determine whether an exception raised by a :class:`Transaction` method is retryable. Returns a :class:`FutureNil`. You **must** call the :meth:`wait()` method on the :class:`FutureNil`, which will return after a delay if the exception was retryable, or re-raise the exception if it was not. + + .. note:: Consider using :meth:`Database.transact`, which calls this method for you. + +.. method:: Transaction.reset() -> nil + + |transaction-reset-blurb| + +.. method:: Transaction.cancel() -> nil + + |transaction-cancel-blurb| + + .. warning :: |transaction-reset-cancel-warning| + + .. warning :: |transaction-commit-cancel-warning| + +Watches +------- + +.. method:: Transaction.watch(key) -> FutureNil + + Creates a watch and returns a :class:`FutureNil` that will become ready when the watch reports a change to the value of the specified key. + + |transaction-watch-blurb| + + |transaction-watch-committed-blurb| + + |transaction-watch-error-blurb| + + |transaction-watch-limit-blurb| + + +Conflict ranges +--------------- + +.. note:: |conflict-range-note| + +|conflict-range-blurb| + +.. method:: Transaction.add_read_conflict_range(begin, end) -> nil + + |add-read-conflict-range-blurb| + +.. method:: Transaction.add_read_conflict_key(key) -> nil + + |add-read-conflict-key-blurb| + +.. method:: Transaction.add_write_conflict_range(begin, end) -> nil + + |add-write-conflict-range-blurb| + +.. method:: Transaction.add_write_conflict_key(key) -> nil + + |add-write-conflict-key-blurb| + +Versions +-------- + +Most applications should use the read version that FoundationDB determines automatically during the transaction's first read, and ignore all of these methods. + +.. method:: Transaction.set_read_version(version) -> nil + + |infrequent| Sets the database version that the transaction will read from the database. The database cannot guarantee causal consistency if this method is used (the transaction's reads will be causally consistent only if the provided read version has that property). + +.. method:: Transaction.get_read_version() -> Version + + |infrequent| Returns the transaction's read version. + +.. method:: Transaction.get_committed_version() -> Integer + + |infrequent| |transaction-get-committed-version-blurb| + +.. method:: Transaction.get_verionstamp() -> String + + |infrequent| |transaction-get-versionstamp-blurb| + +Transaction options +------------------- + +|transaction-options-blurb| + +.. _api-ruby-snapshot-ryw: + +.. method:: Transaction.options.set_snapshot_ryw_disable() -> nil + + |option-snapshot-ryw-disable-blurb| + +.. method:: Transaction.options.set_snapshot_ryw_enable() -> nil + + |option-snapshot-ryw-enable-blurb| + +.. method:: Transaction.options.set_priority_batch() -> nil + + |option-priority-batch-blurb| + +.. method:: Transaction.options.set_priority_system_immediate() -> nil + + |option-priority-system-immediate-blurb| + + .. warning:: |option-priority-system-immediate-warning| + +.. method:: Transaction.options.set_causal_read_risky() -> nil + + |option-causal-read-risky-blurb| + +.. method:: Transaction.options.set_causal_write_risky() -> nil + + |option-causal-write-risky-blurb| + +.. method:: Transaction.options.set_next_write_no_write_conflict_range() -> nil + + |option-next-write-no-write-conflict-range-blurb| + + .. note:: |option-next-write-no-write-conflict-range-note| + +.. method:: Transaction.options.set_read_your_writes_disable() -> nil + + |option-read-your-writes-disable-blurb| + + .. note:: |option-read-your-writes-disable-note| + +.. method:: Transaction.options.set_read_ahead_disable() -> nil + + |option-read-ahead-disable-blurb| + +.. method:: Transaction.options.set_access_system_keys() -> nil + + |option-access-system-keys-blurb| + + .. warning:: |option-access-system-keys-warning| + +.. method:: Transaction.options.set_read_system_keys() -> nil + + |option-read-system-keys-blurb| + + .. warning:: |option-read-system-keys-warning| + +.. method:: Transaction.options.set_retry_limit() -> nil + + |option-set-retry-limit-blurb1| + + |option-set-retry-limit-blurb2| + +.. method:: Transaction.options.set_max_retry_delay() -> nil + + |option-set-max-retry-delay-blurb| + +.. method:: Transaction.options.set_timeout() -> nil + + |option-set-timeout-blurb1| + + |option-set-timeout-blurb2| + + |option-set-timeout-blurb3| + +.. _transact: + +The transact method +=================== + +When performing a database transaction, any read operation, as well as the commit itself, may fail with one of a number of errors. If the error is a retryable error, the transaction needs to be restarted from the beginning. Committing a transaction is also an asynchronous operation, and the returned :class:`FutureNil` object needs to be waited on to ensure that no errors occurred. + +The methods :meth:`Database.transact` and :meth:`Transaction.transact` are convenient wrappers that allow much of this complexity to be handled automatically. A call like :: + + db.transact do |tr| + tr['a'] = 'A' + tr['b'] = 'B' + end + +is equivalent to :: + + tr = db.create_transaction + committed = false + while !committed + begin + tr['a'] = 'A' + tr['b'] = 'B' + tr.commit.wait + committed = true + rescue FDB::Error => e + tr.on_error(e).wait + end + end + +The first form is considerably easier to read, and ensures that the transaction is correctly committed (and retried, when necessary). + +.. note:: Be careful when using control flow constructs within the block passed to :meth:`transact`. ``return`` or ``break`` will exit the retry loop *without committing* the transaction. Use ``next`` to exit the block and commit the transaction. + +The :meth:`Transaction.transact` method, which logically does nothing, makes it easy to write functions that operate on either a :class:`Database` or :class:`Transaction`. Consider the following method:: + + def increment(db_or_tr, key) + db_or_tr.transact do |tr| + tr[key] = (tr[key].to_i + 1).to_s + end + end + +This method can be called with a :class:`Database`, and it will do its job atomically:: + + increment(db, 'number') + +It can also be called by another transactional method with a transaction:: + + def increment_both(db_or_tr, key1, key2) + db_or_tr.transact do |tr| + increment(tr, key1) + increment(tr, key2) + end + end + +In the second case, ``increment`` will use provided transaction and will not commit it or retry errors, since that is the responsibility of its caller, ``increment_both``. + +.. note:: |fdb-transactional-unknown-result-note| + +.. _api-ruby-future: + +Future objects +============== + +|future-blurb1| + +.. _api-ruby-block: + +When a future object "blocks", the ruby thread is blocked, but the global interpreter lock is released. + +When used in a conditional expression, a future object will evaluate to true, even if its value is nil. To test for nil, you must explicitly use the nil?() method:: + + if tr['a'].nil? + +All future objects are a subclass of the :class:`Future` type. + +.. class:: Future + + .. method:: Future.ready?() -> bool + + Immediately returns true if the future object is ready, false otherwise. + + .. method:: Future.block_until_ready() -> nil + + Blocks until the future object is ready. + + .. method:: Future.on_ready() {|future| block } -> nil + + Yields ``self`` to the given block when the future object is ready. If the future object is ready at the time :meth:`on_ready` is called, the block may be called immediately in the current thread (although this behavior is not guaranteed). Otherwise, the call may be delayed and take place on the thread with which the client was initialized. Therefore, the block is responsible for any needed thread synchronization (and/or for posting work to your application's event loop, thread pool, etc., as may be required by your application's architecture). + + .. warning:: |fdb-careful-with-callbacks-blurb| + + .. method:: Future.cancel() -> nil + + |future-cancel-blurb| + + .. classmethod:: Future.wait_for_any(*futures) -> Fixnum + + Does not return until at least one of the given future objects is ready. Returns the index in the parameter list of a ready future object. + +Asynchronous methods return one of the following subclasses of :class:`Future`: + +.. class:: Value + +.. class:: Key + + Both types are future :class:`String` objects. Objects of these types respond to the same methods as objects of type :class:`String`, and may be passed to any method that expects a :class:`String`. + + An implementation quirk of :class:`Value` is that it will never evaluate to ``false``, even if its value is ``nil``. It is important to use ``if value.nil?`` rather than ``if ~value`` when checking to see if a key was not present in the database. + +.. class:: Version + + This type is a future :class:`Integer` object. Objects of this type respond to the same methods as objects of type :class:`Integer`, and may be passed to any method that expects a :class:`Integer`. + +.. class:: FutureArray + + This type is a future :class:`Array` object. Objects of this type respond to the same methods as objects of type :class:`Array`, and may be passed to any method that expects a :class:`Array`. + +.. class:: FutureNil + + This type is a future returned from asynchronous methods that logically have no return value. + + .. method:: FutureNil.wait() -> nil + + For a :class:`FutureNil` object returned by :meth:`Transaction.commit` or :meth:`Transaction.on_error`, you must call :meth:`FutureNil.wait`, which will return ``nil`` if the operation succeeds or raise an :exc:`FDB::Error` if an error occurred. Failure to call :meth:`FutureNil.wait` on a returned :class:`FutureNil` object means that any potential errors raised by the asynchronous operation that returned the object *will not be seen*, and represents a significant error in your code. + +.. _ruby streaming mode: + +Streaming modes +=============== + +|streaming-mode-blurb1| + +|streaming-mode-blurb2| + +The following streaming modes are available: + +.. const:: :iterator + + *The default.* The client doesn't know how much of the range it is likely to used and wants different performance concerns to be balanced. + + Only a small portion of data is transferred to the client initially (in order to minimize costs if the client doesn't read the entire range), and as the caller iterates over more items in the range larger batches will be transferred in order to maximize throughput. + +.. const:: :want_all + + The client intends to consume the entire range and would like it all transferred as early as possible. + +.. const:: :small + + |infrequent| Transfer data in batches small enough to not be much more expensive than reading individual rows, to minimize cost if iteration stops early. + +.. const:: :medium + + |infrequent| Transfer data in batches sized in between ``:small`` and ``:large``. + +.. const:: :large + + |infrequent| Transfer data in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the client stops iteration early, some disk and network bandwidth may be wasted. The batch size may still be too small to allow a single client to get high throughput from the database, so if that is what you need consider ``:serial``. + +.. const:: :serial + + Transfer data in batches large enough that an individual client can get reasonable read bandwidth from the database. If the client stops iteration early, considerable disk and network bandwidth may be wasted. + +.. const:: :exact + + |infrequent| The client has passed a specific row limit and wants that many rows delivered in a single batch. This is not particularly useful in Ruby because enumerable functionality makes batches of data transparent, so use ``:want_all`` instead. + +Errors +====== + +Errors in the FoundationDB API are raised as exceptions of type :class:`FDB::Error`. These errors may be displayed for diagnostic purposes, but generally should be passed to :meth:`Transaction.on_error`. When using :meth:`Database.transact`, appropriate errors will be retried automatically. + +.. class:: Error + + .. attr_reader:: code + + A :class:`Fixnum` associated with the error type. + + .. method:: description() -> String + + Returns a somewhat human-readable description of the error. + +.. warning:: You should only use the :attr:`code` attribute for programmatic comparisons, as the description of the error may change at any time. Whenever possible, use the :meth:`Transaction.on_error` method to handle :class:`FDB::Error` exceptions. + +.. _api-ruby-tuple-layer: + +Tuple layer +=========== + +.. module:: FDB::Tuple + +|tuple-layer-blurb| + +.. note:: |tuple-layer-note| + +In the FoundationDB Ruby API, a tuple is an :class:`Enumerable` of elements of the following data types: + ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Type | Legal Values | Canonical Value | ++======================+=============================================================================+==============================================================================+ +| Null value | ``nil`` | ``nil`` | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Byte string | Any value ``v`` where ``v.kind_of? String == true`` and ``v.encoding`` is | ``String`` with encoding ``Encoding::ASCII_8BIT`` | +| | either ``Encoding::ASCII_8BIT`` (aka ``Encoding::BINARY``) or | | +| | ``Encoding::US_ASCII`` (aka ``Encoding::ASCII``) | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Unicode string | Any value ``v`` where ``v.kind_of? String == true`` and ``v.encoding`` is | ``String`` with encoding ``Encoding::UTF_8`` | +| | ``Encoding::UTF_8`` | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| 64-bit signed integer| Any value ``v`` where ``v.kind_of? Integer == true`` and ``-2**64+1 <= v <= | ``Fixnum`` or ``Bignum`` (depending on the magnitude of the value) | +| | 2**64-1`` | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Floating point number| Any value ``v`` where ``v.kind_of? FDB::Tuple::SingleFloat`` where | :class:`FDB::Tuple::SingleFloat` | +| (single-precision) | ``v.value.kind_of? Float`` and ``v.value`` fits inside an IEEE 754 32-bit | | +| | floating-point number. | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Floating point number| Any value ``v`` where ``v.kind_of? Float`` | ``Float`` | +| (double-precision) | | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Boolean | Any value ``v`` where ``v.kind_of? Boolean`` | ``Boolean`` | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| UUID | Any value ``v`` where ``v.kind_of? FDB::Tuple::UUID`` where | :class:`FDB::Tuple::UUID` | +| | ``v.data.kind_of? String`` and ``v.data.encoding`` is ``Encoding::BINARY`` | | +| | and ``v.data.length == 16`` | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ +| Array | Any value ``v`` such that ``v.kind_of? Array`` and each element within | ``Array`` | +| | ``v`` is one of the supported types with a legal value. | | ++----------------------+-----------------------------------------------------------------------------+------------------------------------------------------------------------------+ + +Note that as Ruby does not have native support for single-precision floating point values and UUIDs, tuple elements +of those types are returned instead as :class:`FDB::Tuple::SingleFloat` and :class:`FDB::Tuple::UUID` instances. +These are simple classes that just wrap their underlying values, and they are not intended to offer all of the methods +that a more fully-featured library for handling floating point values or UUIDs might offer. Most applications +should use their library of choice for handling these values and then convert to the appropriate tuple-type +when serializing for storage into the key-value store. + +A single tuple element is ordered first by its type, and then by its value. + +If ``T`` is an :class:`Enumerable` meeting these criteria, then conceptually:: + + T == FDB::Tuple.unpack(FDB::Tuple.pack(T)) + +.. note:: Unpacking a tuple always returns an :class:`Array` of elements in a canonical representation, so packing and then unpacking a tuple may result in an equivalent but not identical representation. + +.. function:: pack(tuple) -> String + + Returns a key encoding the given tuple. + +.. function:: unpack(key) -> Array + + Returns the tuple encoded by the given key. Each element in the :class:`Array` will either be ``nil``, a :class:`String` (with encoding ``Encoding::ASCII_8BIT`` for byte strings or ``Encoding::UTF_8`` for unicode strings), or a :class:`Fixnum` or :class:`Bignum` for integers, depending on the magnitude. + +.. function:: range(tuple) -> Array + + Returns the range containing all keys that encode tuples strictly starting with ``tuple`` (that is, all tuples of greater length than tuple of which tuple is a prefix). + + The range will be returned as an :class:`Array` of two elements, and may be used with any FoundationDB methods that require a range:: + + r = FDB::Tuple.range(T) + tr.get_range(r[0], r[1]) { |kv| ... } + +.. class:: FDB::Tuple::SingleFloat(value) + + Wrapper around a single-precision floating point value. The ``value`` parameter should be a ``Float`` that + can be encoded as an IEEE 754 floating point number. If the float does not fit within a IEEE 754 floating point + integer, there may be a loss of precision. + +.. attr_reader:: FDB::Tuple::SingleFloat.value + + The underlying value of the ``SingleFloat`` object. This should have type ``Float``. + +.. function:: FDB::Tuple::SingleFloat.<=> -> Fixnum + + Comparison method for ``SingleFloat`` objects. This will compare the values based on their float value. This + will sort the values in a manner consistent with the way items are sorted as keys in the database, which can + be different from standard float comparison in that -0.0 is considered to be strictly less than 0.0 and NaN + values are sorted based on their byte representation rather than being considered incomparable. + +.. function:: FDB::Tuple::SingleFloat.to_s -> String + + Creates a string representation of the ``SingleFloat``. This will just return the string representation of the underlying + value. + +.. class:: FDB::Tuple::UUID(data) + + Wrapper around a 128-bit UUID. The ``data`` parameter should be a byte string of length 16 and is taken to + be the big-endian byte representation of the UUID. If ``data`` is not of length 16, an |error-type| is thrown. + +.. attr_reader:: FDB::Tuple::UUID.data + + The UUID data as a byte array of length 16. This is stored in big-endian order. + +.. method:: FDB::Tuple::UUID.<=> -> Fixnum + + Comparison method for ``UUID`` objects. It will compare the UUID representations as unsigned byte arrays. + This is the same order the database uses when comparing serialized UUIDs when they are used as part + of a key. + +.. method:: FDB::Tuple::UUID.to_s -> String + + Creates a string representation of the ``UUID``. This will be a string of length 32 containing the hex representation + of the UUID bytes. + +.. _api-ruby-subspaces: + +Subspaces +========= + +.. currentmodule:: FDB + +|subspace-blurb1| + +|subspace-blurb2| + +.. note:: For general guidance on subspace usage, see the discussion in the :ref:`Developer Guide `. + +.. class:: Subspace(prefix_tuple=[], raw_prefix='') + + |subspace-blurb3| + +.. method:: Subspace.key() -> String + + |subspace-key-blurb| + +.. method:: Subspace.pack(tuple) -> String + + |subspace-pack-blurb| + +.. method:: Subspace.unpack(key) -> Array + + |subspace-unpack-blurb| + +.. method:: Subspace.range(tuple=[]) -> Array + + |subspace-range-blurb| + + The range will be returned as an :class:`Array` of two elements, and may be used with any FoundationDB methods that require a range:: + + r = subspace.range(['A', 2]) + tr.get_range(r[0], r[1]) { |kv| ... } + +.. method:: Subspace.contains?(key) -> bool + + |subspace-contains-blurb| + +.. method:: Subspace.as_foundationdb_key() -> String + + |subspace-as-foundationdb-key-blurb| This method serves to support the :ref:`as_foundationdb_key() ` convenience interface. + +.. method:: Subspace.subspace(tuple) -> Subspace + + |subspace-subspace-blurb| + +.. method:: Subspace.[](item) -> Subspace + + Shorthand for Subspace.subspace([item]). This function can be combined with the :meth:`Subspace.as_foundationdb_key()` convenience to turn this:: + + s = FDB::Subspace.new(['x']) + tr[s.pack(['foo', 'bar', 1])] = '' + + into this:: + + s = FDB::Subspace.new(['x']) + tr[s['foo']['bar'][1]] = '' + +.. _api-ruby-directories: + +Directories +=========== + +|directory-blurb1| + +.. note:: For general guidance on directory usage, see the discussion in the :ref:`Developer Guide `. + +|directory-blurb2| + +|directory-blurb3| + +.. global:: FDB::directory + + The default instance of :class:`DirectoryLayer`. + +.. class:: DirectoryLayer(node_subspace=Subspace.new([], "\\xFE"), content_subspace=Subspace.new, allow_manual_prefixes=false) + + |directory-layer-blurb| + +.. method:: DirectoryLayer.create_or_open(db_or_tr, path, options={}) -> DirectorySubspace + + |directory-create-or-open-blurb| + + If the byte string ``:layer`` is specified in ``options`` and the directory is new, it is recorded as the layer; if ``:layer`` is specified and the directory already exists, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.open(db_or_tr, path, options={}) -> DirectorySubspace + + |directory-open-blurb| + + If the byte string ``:layer`` is specified in ``options``, it is compared against the layer specified when the directory was created, and the method will |error-raise-type| an |error-type| if they differ. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.create(db_or_tr, path, options={}) -> DirectorySubspace + + |directory-create-blurb| + + If the byte string ``:prefix`` is specified in ``options``, the directory is created with the given physical prefix; otherwise a prefix is allocated automatically. + + If the byte string ``:layer`` is specified in ``options``, it is recorded with the directory and will be checked by future calls to open. + + |directory-create-or-open-return-blurb| + +.. method:: DirectoryLayer.move(db_or_tr, old_path, new_path) -> DirectorySubspace + + |directory-move-blurb| + + |directory-move-return-blurb| + +.. method:: DirectoryLayer.remove(db_or_tr, path) -> bool + + |directory-remove-blurb| + + .. warning:: |directory-remove-warning| + +.. method:: DirectoryLayer.remove_if_exists(db_or_tr, path) -> bool + + |directory-remove-if-exists-blurb| + + .. warning:: |directory-remove-warning| + +.. method:: DirectoryLayer.list(db_or_tr, path=[]) -> Enumerable + + Returns an :class:`Enumerable` of names of the immediate subdirectories of the directory at ``path``. Each name is a unicode string representing the last component of a subdirectory's path. + +.. method:: DirectoryLayer.exists?(db_or_tr, path) -> bool + + |directory-exists-blurb| + +.. method:: DirectoryLayer.layer() -> String + + |directory-get-layer-blurb| + +.. method:: DirectoryLayer.path() -> Enumerable + + |directory-get-path-blurb| + +.. _api-ruby-directory-subspace: + +DirectorySubspace +----------------- + +.. class:: DirectorySubspace + + |directory-subspace-blurb| + +.. method:: DirectorySubspace.move_to(db_or_tr, new_absolute_path) -> DirectorySubspace + + |directory-move-to-blurb| + + |directory-move-return-blurb| + +Locality information +==================== + +.. module:: FDB::Locality + +|locality-api-blurb| + +.. function:: get_boundary_keys(db_or_tr, begin, end) -> Enumerator + + |locality-get-boundary-keys-blurb| + + |locality-get-boundary-keys-db-or-tr| + + Like a |future-object|, the returned :class:`Enumerator` issues asynchronous read operations to fetch data in the range, and may block while enumerating its values if the read has not completed. + + |locality-get-boundary-keys-warning-danger| + +.. function:: get_addresses_for_key(tr, key) -> Array + + |locality-get-addresses-for-key-blurb| diff --git a/documentation/sphinx/source/architecture.rst b/documentation/sphinx/source/architecture.rst index be0b7a28672..a730e0b4923 100644 --- a/documentation/sphinx/source/architecture.rst +++ b/documentation/sphinx/source/architecture.rst @@ -3,7 +3,7 @@ Architecture ############ FoundationDB makes your architecture flexible and easy to operate. Your applications can send their data directly to the FoundationDB or to a :doc:`layer`, a user-written module that can provide a new data model, compatibility with existing systems, or even serve as an entire framework. In both cases, all data is stored in a single place via an ordered, transactional key-value API. - + The following diagram details the logical architecture. -.. image:: /images/Architecture.pdf \ No newline at end of file +.. image:: /images/Architecture.png diff --git a/documentation/sphinx/source/backups.rst b/documentation/sphinx/source/backups.rst index 2a72530936b..7c1c27860e0 100644 --- a/documentation/sphinx/source/backups.rst +++ b/documentation/sphinx/source/backups.rst @@ -1,521 +1,543 @@ -.. _backups: - -###################### -Backup, Restore, and Replication for Disaster Recovery -###################### - -.. include:: guide-common.rst.inc - -This document covers backup and restoration of a FoundationDB database. While FoundationDB itself is fault tolerant, the backup tool provides an additional level of protection by supporting recovery from disasters or unintentional modification of the database. - -.. _backup-introduction: - -Introduction -============ - -FoundationDB's backup tool makes a consistent, point-in-time backup of a FoundationDB database without downtime. Like FoundationDB itself, the backup/restore software is distributed, with multiple backup agents cooperating to perform a backup or restore faster than a single machine can send or receive data and to continue the backup process seamlessly even when some backup agents fail. - -The FoundationDB database usually cannot maintain a consistent snapshot long enough to read the entire database, so full backup consists of an *inconsistent* copy of the data with a log of database changes that took place during the creation of that inconsistent copy. During a restore, the inconsistent copy and the log of changes are combined to reconstruct a consistent, point-in-time snapshot of the original database. - -A FoundationDB Backup job can run continuously, pushing multiple inconsistent snapshots and logs of changes over time to maintain the backup's restorable point-in-time very close to now. - -Backup vs DR -============ - -FoundationDB can backup a database to local disks, a blob store (such as Amazon S3), or to another FoundationDB database. - -Backing up one database to another is a special form of backup is called DR backup or just DR for short. DR stands for Disaster Recovery, as it can be used to keep two geographically separated databases in close synchronization to recover from a catastrophic disaster. Once a DR operation has reached 'differential' mode, the secondary database (the destination of the DR job) will always contains a *consistent* copy of the primary database (the source of the DR job) but it will be from some past point in time. If the primary database is lost and applications continue using the secondary database, the "ACI" in ACID is preserved but D (Durability) is lost for some amount of most recent changes. When DR is operating normally, the secondary database will lag behind the primary database by as little as a few seconds worth of database commits. - -While a cluster is being used as the destination for a DR operation it will be locked to prevent accidental use or modification. - -Limitations -=========== - -Backup data is not encrypted on disk, in a blob store account, or in transit to a destination blob store account or database. - -Tools -=========== - -There are 5 command line tools for working with Backup and DR operations: - -``fdbbackup`` - This command line tool is used to control (but not execute) backup jobs and manage backup data. It can ``start`` or ``abort`` a backup, ``discontinue`` a continuous backup, get the ``status`` of an ongoing backup, or ``wait`` for a backup to complete. It can also ``describe``, ``delete``, ``expire`` data in a backup, or ``list`` the backups at a destination folder URL. - -``fdbrestore`` - This command line tool is used to control (but not execute) restore jobs. It can ``start`` or ``abort`` a restore, get the ``status`` of current and recent restore tasks, or ``wait`` for a restore task to complete while printing ongoing progress details. - -``backup_agent`` - The backup agent is a daemon that actually executes the work of the backup and restore jobs. Any number of backup agents pointed at the same database will cooperate to perform backups and restores. The Backup URL specified for a backup or restore must be accessible by all ``backup_agent`` processes. - -``fdbdr`` - This command line tool is used to control (but not execute) DR jobs - backups from one database to anothher. It can ``start``, ``abort`` a DR job, or ``switch`` the DR direction. It can also get the ``status`` of a running DR job. - -``dr_agent`` - The database backup agent is a daemon that actually executes the work of the DR jobs, writing snapshot and log data to the destination database. Any number of agents pointed at the same databases will cooperate to perform the backup. - -By default, the FoundationDB packages are configured to start a single ``backup_agent`` process on each FoundationDB server. If you want to perform a backup to a network drive or blob store instance that is accessible to every server, you can immediately use the ``fdbbackup start`` command from any machine with access to your cluster to start the backup - -:: - - user@host$ fdbbackup start -d - -If instead you want to perform a backup to the local disk of a particular machine or machines which are not network accessible to the FoundationDB servers, then you should disable the backup agents on the FoundationDB servers. This is accomplished by commenting out all of the ``[backup_agent.]`` sections in :ref:`foundationdb.conf `. Do not comment out the global ``[backup_agent]`` section. Next, start backup agents on the destination machine or machines. Now, when you start a backup, you can specify the destination directory (as a Backup URL) using a local path on the destination machines. The backup agents will fetch data from the database and store it locally on the destination machines. - -Backup URLs -=========== - -Backup and Restore locations are specified by Backup URLs. Currently there are two valid Backup URL formats. - -Note that items in angle brackets (< and >) are just placeholders and must be replaced (including the brackets) with meaningful values. Items within square brackets ([ and ]) are optional. - -For local directories, the Backup URL format is - -:: - - file:// - -An example would be ``file:///home/backups`` which would refer to the directory ``/home/backups``. -Note that since paths must be absolute this will result in three slashes (/) in a row in the URL. - -Note that for local directory URLs the actual backup files will not be written to directly but rather to a uniquely timestamped subdirectory. When starting a restore the path to the timestamped subdirectory must be specified. - -For blob store backup locations, the Backup URL format is - -:: - - blobstore://[:]@[:]/[?=[&=]...] - - - API key to use for authentication - - API key's secret. Optional. - - Remote hostname or IP address to connect to - - Remote port to connect to. Optional. Default is 80. - - Name of backup. It can contain '/' characters, to place backups into a folder-like structure. - - = - Optional URL parameters. See below for details. - -If is not specified, it will be looked up in :ref:`blob credential sources`. - -An example blob store Backup URL would be ``blobstore://myKey:mySecret@something.domain.com:80/dec_1_2017_0400``. - -Blob store Backup URLs can have optional parameters at the end which set various limits on interactions with the blob store. All values must be positive decimal integers. The default values are not very restrictive. The most likely parameter a user would want to change is ``max_send_bytes_per_second`` (or ``sbps`` for short) which determines the upload speed to the blob service. - -Here is a complete list of valid parameters: - - *connect_tries* (or *ct*) - Number of times to try to connect for each request. - - *request_tries* (or *rt*) - Number of times to try each request until a parseable HTTP response other than 429 is received. - - *requests_per_second* (or *rps*) - Max number of requests to start per second. - - *concurrent_requests* (or *cr*) - Max number of requests in progress at once. - - *multipart_max_part_size* (or *maxps*) - Max part size for multipart uploads. - - *multipart_min_part_size* (or *minps*) - Min part size for multipart uploads. - - *concurrent_uploads* (or *cu*) - Max concurrent uploads (part or whole) that can be in progress at once. - - *concurrent_reads_per_file* (or *crps*) - Max concurrent reads in progress for any one file. - - *read_block_size* (or *rbs*) - Block size in bytes to be used for reads. - - *read_ahead_blocks* (or *rab*) - Number of blocks to read ahead of requested offset. - - *read_cache_blocks_per_file* (or *rcb*) - Size of the read cache for a file in blocks. - - *max_send_bytes_per_second* (or *sbps*) - Max send bytes per second for all requests combined. - - *max_recv_bytes_per_second* (or *rbps*) - Max receive bytes per second for all requests combined - -.. _blob-credential-files: - -Blob Credential Files -============================== - -In order to help safeguard blob store credentials, the can optionally be omitted from blobstore:// URLs on the command line. Omitted secrets will be resolved at connect time using 1 or more Blob Credential files. - -Blob Credential files can be specified on the command line (via --blob_credentials ) or via the environment variable FDB_BLOB_CREDENTIALS which can be set to a colon-separated list of files. The command line takes priority over the environment variable however all files from both sources will be used. - -At connect time, the specified files are read in order and the first matching account specification (user@host) -will be used to obtain the secret key. - -The Blob Credential File format is JSON with the following schema: - -:: - - { - "accounts" : { - "user@host" : { "secret" : "SECRETKEY" }, - "user2@host2" : { "secret" : "SECRETKEY2" } - } - } - - -``fdbbackup`` command line tool -=============================== - -.. program:: fdbbackup - -The ``fdbbackup`` command line tool is used to control backup jobs or to manage backup data. - -:: - - user@host$ fdbbackup [-h] - -The following options apply to most subcommands: - -``-C `` - Path to the cluster file that should be used to connect to the FoundationDB cluster you want to use. If not specified, a :ref:`default cluster file ` will be used. - -``-d `` - The Backup URL which the subcommand should read, write, or modify. For ``start`` operations, the Backup URL must be accessible by the ``backup_agent`` processes. - -``-t `` - A "tag" is a named slot in which a backup task executes. Backups on different named tags make progress and are controlled independently, though their executions are handled by the same set of backup agent processes. Any number of unique backup tags can be active at once. It the tag is not specified, the default tag name "default" is used. - -``--blob_credentials `` - Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. - -.. _backup-start: - -.. program:: fdbbackup start - -``start`` ---------- - -The ``start`` subcommand is used to start a backup. If there is already a backup in progress, the command will fail and the current backup will be unaffected. Otherwise, a backup is started. If the wait option is used, the command will wait for the backup to complete; otherwise, it returns immediately. - -:: - - user@host$ fdbbackup start [-t ] -d [-z] [-s ] [-w] [-k '[ ]']... - -``-z`` - Perform the backup continuously rather than terminating once a restorable backup is achieved. Database mutations within the backup's target key ranges will be continuously written to the backup as well as repeated inconsistent snapshots at the configured snapshot rate. - -``-s `` or ``--snapshot_interval `` - Specifies the duration, in seconds, of the inconsistent snapshots written to the backup in continous mode. The default is 864000 which is 10 days. - -``-w`` - Wait for the backup to complete with behavior identical to that of the :ref:`wait command `. - -``-k '[ ]'`` - Specify a key range to be included in the backup. Can be used multiple times to specify multiple key ranges. The argument should be a single string containing either a BEGIN alone or both a BEGIN and END separated by a space. If only the BEGIN is specified, the END is assumed to be BEGIN + '\xff'. If no key ranges are different, the default is all user keys ('' to '\xff'). - - Each key range should be quoted in a manner appropriate for your command line environment. Here are some examples for Bash: :: - - user@host$ fdbbackup start -k 'apple bananna' -k 'mango pineapple' -d - user@host$ fdbbackup start -k '@pp1e b*n*nn*' -k '#an&0 p^n3app!e' -d - -.. program:: fdbbackup abort - -``abort`` ---------- - -The ``abort`` subcommand is used to abort a backup that is currently in progress. If there is no backup in progress, the command will return an error. The destination backup is NOT deleted automatically, and it may or may not be restorable depending on when the abort is done. - -:: - - user@host$ fdbbackup abort [-t ] - -.. program:: fdbbackup discontinue - -``discontinue`` ---------------- - -The ``discontinue`` subcommand is only available for backups that were started with the continuous (``-z``) option. Its effect is to discontinue the continous backup. Note that the subcommand does *not* abort the backup; it simply allows the backup to complete as a noncontinuous backup would. - -:: - - user@host$ fdbbackup discontinue [-t ] [-w] - -``-w`` - Wait for the backup to complete with behavior identical to that of the :ref:`wait command `. - -.. _backup-wait: - -.. program:: fdbbackup wait - -``wait`` --------- - -The ``wait`` subcommand is used to wait for a backup to complete, which is useful for scripting purposes. If there is a backup in progress, it waits for it to complete or be aborted and returns a status based on the result of the backup. If there is no backup in progress, it returns immediately based on the result of the previous backup. The exit code is zero (success) if the backup was completed successfully and nonzero if it was aborted. - -:: - - user@host$ fdbbackup wait [-t ] - -.. program:: fdbbackup status - -``status`` ----------- - -The ``status`` subcommand is used to get information on the current status of backup. It will show several backup metrics as well as recent errors which organized by whether or not they appear to be preventing backup progress. - -:: - - user@host$ fdbbackup status [-t ] - -.. program:: fdbbackup delete - -``delete`` ----------- - -The ``delete`` subcommand will delete the specified backup. - -:: - - user@host$ fdbbackup delete -d - -.. warning:: If you cancel a delete operation while it is in progress the specified backup is in an unknown state and is likely no longer usable. Repeat the delete command to finish deleting the backup. - -.. program:: fdbbackup expire - -``expire`` ----------- - -The ``expire`` subcommand will remove data from a backup prior to some point in time referred to as the 'cutoff'. - -:: - - user@host$ fdbbackup expire -d [] [--force] - -The expiration CUTOFF must be specified by one of the two following arguments: - - ``--expire_before_timestamp `` - Specifies the expiration cutoff to DATETIME. Requires a cluster file and will use version/timestamp metadata in the database to convert DATETIME to a database commit version. DATETIME must be in the form "YYYY-MM-DD.HH:MI:SS" in UTC. - - ``--expire_before_version `` - Specifies the cutoff by a database commit version. - -Optionally, the user can specify a minimum RESTORABILITY guarauntee with one of the following options. - - ``--restorable_after_timestamp `` - Specifies that the backup must be restorable to DATETIME and later. Requires a cluster file and will use version/timestamp metadata in the database to convert DATETIME to a database commit version. DATETIME must be in the form "YYYY-MM-DD.HH:MI:SS" in UTC. - - ``--restorable_after_version `` - Specifies that the backup must be restorable as of VERSION and later. - -``-f`` or ``--force`` - If the designated cutoff will result in removal of data such that the backup's restorability would be reduced to either unrestorable or less restorable than the optional restorability requirement then the --force option must be given or the result will be an error and no action will be taken. - -.. program:: fdbbackup describe - -``describe`` ----------- - -The ``describe`` subcommand will analyze the given backup and print a summary of the snapshot and mutation data versions it contains as well as the version range of restorability the backup can currently provide. - -:: - - user@host$ fdbbackup describe -d [--version_timestamps] [-C ] - -``--version_timestamps`` - If the originating cluster is still available and is passed on the command line, this option can be specified in order for all versions in the output to also be converted to timestamps for better human readability. - - -.. program:: fdbbackup list - -``list`` ----------- - -The ``list`` subcommand will list the backups at a given 'base' or shortened Backup URL. - -:: - - user@host$ fdbbackup list -b - -``-b `` or ``--base_url `` - This a shortened Backup URL which looks just like a Backup URL but without the backup name so that the list command will discover and list all of the backups under that base URL. - - -``fdbrestore`` command line tool -================================ - -.. program:: fdbrestore - -The ``fdbrestore`` command line tool is used to control restore tasks. Note that a restore operation will not clear the target key ranges, for safety reasons, so you must manually clear the ranges to be restored prior to starting the restore. - -.. warning:: It is your responsibility to ensure that no clients are accessing the database while it is being restored. During the restore process the database is in an inconsistent state, and writes that happen during the restore process might be partially or completely overwritten by restored data. - -:: - - user@host$ fdbrestore (start | abort | wait | status) [OPTIONS] - -The following options apply to all commands: - -``-h`` - Get help on the ``fdbrestore`` command. - -``-t `` - Specify the tag for the restore task. Multiple restore tasks can be in progress at once so long as each task uses a different tag. The default tag is "default". - - .. warning:: If multiple restore tasks are in progress they should be restoring to different prefixes or the result is undefined. - -``-C `` - Path to the cluster file that should be used to connect to the FoundationDB cluster you want to use. If not specified, a :ref:`default cluster file ` will be used. - -``--blob_credentials `` - Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. - -.. _restore-start: - -``start`` ---------- - -The ``start`` command will start a new restore on the specified (or default) tag. The command will fail if a tag is already in use by an active restore. - -:: - user@host$ fdbrestore start -r [OPTIONS] - -``-r `` - Required. Specifies the Backup URL for the source backup data to restore to the database. The source data must be accessible by the ``backup_agent`` processes for the cluster. - -``-w`` - Wait for the the restore to reach a final state (such as complete) before exiting. Prints a progress update every few seconds. Behavior is identical to that of the wait command. - -``-k `` - Specify list of key ranges from the backup to restore to the database - -``--remove_prefix `` - Remove PREFIX from the keys read from the backup - -``--add_prefix `` - Add PREFIX to restored keys before writing them to the database - -``-n`` - Perform a trial run without actually restoring any data. - -``-v `` - Instead of the latest version the backup can be restored to, restore to VERSION. - -.. program:: fdbrestore abort - -``abort`` ---------- - -The ``abort`` command will stop an active backup on the specified (or default) tag. It will display the final state of the restore tag. - -``wait`` --------- - -The ``wait`` command will wait for the restore on the specified (or default) tag to reach a final state (such as complete or abort) and then exit. While waiting it will prints a progress update every few seconds. - -.. program:: fdbrestore status - -``status`` ----------- - -The ``status`` command will print a detailed status report on restore job progress. If a tag is specified, it will only show status for that specific tag, otherwise status for all tags will be shown. - -``backup_agent`` command line tool -================================== - -.. program:: backup_agent - -``backup_agent`` is started automatically on each server in the default configuration of FoundationDB, so you will not normally need to invoke it at the command line. One case in which you would need to do so would be to perform a backup to a destination which is not accessible via a shared filesystem. :: - - user@host$ backup_agent [-h] [-v] [-C ] - -``-h`` - Get help on the ``backup_agent`` command. - -``-v`` - Get the version of FoundationDB in use. - -``-C `` - Specify the path to the ``fdb.cluster`` file that should be used to connect to the FoundationDB cluster you want to back up. - - If not specified, a :ref:`default cluster file ` will be used. - -``--blob_credentials `` - Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. - -.. _fdbdr-intro: - -``fdbdr`` command line tool -=========================== - -.. program:: fdbdr - -The ``fdbdr`` command line tool is used to manage DR tasks. - -:: - - user@host$ fdbdr [-h] [] -d -s [-v] - -The following arguments are used by mutiple subcommands: - -``-h`` - Get help on the ``fdbdr`` command. - -``-v`` - Get the version of FoundationDB in use. - -``-d `` - Specify the path to the ``fdb.cluster`` file for the destination cluster of the DR operaiton. - -``-s `` - Specify the path to the ``fdb.cluster`` file for the source cluster of the DR operation. - -.. _dr-start: - -.. program:: fdbdr start - -``start`` ---------- - -The ``start`` subcommand is used to start a DR backup. If there is already a DR backup in progress, the command will fail and the current DR backup will be unaffected. - -``-k '[ ]'`` - Specify a key range to be included in the DR. Can be used multiple times to specify multiple key ranges. The argument should be a single string containing either a BEGIN alone or both a BEGIN and END separated by a space. If only the BEGIN is specified, the END is assumed to be BEGIN + '\xff'. If no key ranges are different, the default is all user keys ('' to '\xff'). - -.. program:: fdbdr switch - -``switch`` ----------- - -The ``switch`` subcommand is used to swap the source and destination database clusters of an active DR in differential mode. This means the destination will be unlocked and start streaming data into the source database, which will subsequently be locked. - -This command requires both databases to be available. While the switch command is working, both databases will be locked for a few seconds. - -.. program:: fdbdr abort - -``abort`` ---------- - -The ``abort`` subcommand is used to abort a DR that is currently in progress. If there is no backup in progress, the command will return an error. If the DR had already reached differential status, the abort command will leave the destination database at consistent snapshot of the source database from sometime in the past. - - .. warning:: The ``abort`` command will lose some amount of prior commits. - -.. program:: fdbdr status - -``status`` ----------- - -The ``status`` subcommand is used to get information on the current status of DR backup. It tells whether or not there is a DR in progress and whether or not there are active DR agents. It will also report any errors that have been encountered by the DR agents. - -``-e `` - Print the last (up to) ```` errors that were logged into the database by backup agents. The default is 10. - -``dr_agent`` command line tool -============================== - -.. program:: dr_agent - -Unlike ``backup_agent``, ``dr_agent`` is not started automatically in a default FoundationDB installation. A ``dr_agent`` needs the cluster files for both the source database and the destination database, and can only perform a backup in one direction (from source to destination) at a time. - -:: - - user@host$ dr_agent [-h] [-v] -d -s - -``-h`` - Get help on the ``fdbdr`` command. - -``-v`` - Get the version of FoundationDB in use. - -``-d `` - Specify the path to the ``fdb.cluster`` file for the destination cluster of the DR operaiton. - -``-s `` - Specify the path to the ``fdb.cluster`` file for the source cluster of the DR operation. +.. _backups: + +###################### +Backup, Restore, and Replication for Disaster Recovery +###################### + +.. include:: guide-common.rst.inc + +This document covers backup and restoration of a FoundationDB database. While FoundationDB itself is fault tolerant, the backup tool provides an additional level of protection by supporting recovery from disasters or unintentional modification of the database. + +.. _backup-introduction: + +Introduction +============ + +FoundationDB's backup tool makes a consistent, point-in-time backup of a FoundationDB database without downtime. Like FoundationDB itself, the backup/restore software is distributed, with multiple backup agents cooperating to perform a backup or restore faster than a single machine can send or receive data and to continue the backup process seamlessly even when some backup agents fail. + +The FoundationDB database usually cannot maintain a consistent snapshot long enough to read the entire database, so full backup consists of an *inconsistent* copy of the data with a log of database changes that took place during the creation of that inconsistent copy. During a restore, the inconsistent copy and the log of changes are combined to reconstruct a consistent, point-in-time snapshot of the original database. + +A FoundationDB Backup job can run continuously, pushing multiple inconsistent snapshots and logs of changes over time to maintain the backup's restorable point-in-time very close to now. + +Backup vs DR +============ + +FoundationDB can backup a database to local disks, a blob store (such as Amazon S3), or to another FoundationDB database. + +Backing up one database to another is a special form of backup is called DR backup or just DR for short. DR stands for Disaster Recovery, as it can be used to keep two geographically separated databases in close synchronization to recover from a catastrophic disaster. Once a DR operation has reached 'differential' mode, the secondary database (the destination of the DR job) will always contains a *consistent* copy of the primary database (the source of the DR job) but it will be from some past point in time. If the primary database is lost and applications continue using the secondary database, the "ACI" in ACID is preserved but D (Durability) is lost for some amount of most recent changes. When DR is operating normally, the secondary database will lag behind the primary database by as little as a few seconds worth of database commits. + +While a cluster is being used as the destination for a DR operation it will be locked to prevent accidental use or modification. + +Limitations +=========== + +Backup data is not encrypted on disk, in a blob store account, or in transit to a destination blob store account or database. + +Tools +=========== + +There are 5 command line tools for working with Backup and DR operations: + +``fdbbackup`` + This command line tool is used to control (but not execute) backup jobs and manage backup data. It can ``start`` or ``abort`` a backup, ``discontinue`` a continuous backup, get the ``status`` of an ongoing backup, or ``wait`` for a backup to complete. It can also ``describe``, ``delete``, ``expire`` data in a backup, or ``list`` the backups at a destination folder URL. + +``fdbrestore`` + This command line tool is used to control (but not execute) restore jobs. It can ``start`` or ``abort`` a restore, get the ``status`` of current and recent restore tasks, or ``wait`` for a restore task to complete while printing ongoing progress details. + +``backup_agent`` + The backup agent is a daemon that actually executes the work of the backup and restore jobs. Any number of backup agents pointed at the same database will cooperate to perform backups and restores. The Backup URL specified for a backup or restore must be accessible by all ``backup_agent`` processes. + +``fdbdr`` + This command line tool is used to control (but not execute) DR jobs - backups from one database to another. It can ``start``, ``abort`` a DR job, or ``switch`` the DR direction. It can also get the ``status`` of a running DR job. + +``dr_agent`` + The database backup agent is a daemon that actually executes the work of the DR jobs, writing snapshot and log data to the destination database. Any number of agents pointed at the same databases will cooperate to perform the backup. + +By default, the FoundationDB packages are configured to start a single ``backup_agent`` process on each FoundationDB server. If you want to perform a backup to a network drive or blob store instance that is accessible to every server, you can immediately use the ``fdbbackup start`` command from any machine with access to your cluster to start the backup + +:: + + user@host$ fdbbackup start -d + +If instead you want to perform a backup to the local disk of a particular machine or machines which are not network accessible to the FoundationDB servers, then you should disable the backup agents on the FoundationDB servers. This is accomplished by commenting out all of the ``[backup_agent.]`` sections in :ref:`foundationdb.conf `. Do not comment out the global ``[backup_agent]`` section. Next, start backup agents on the destination machine or machines. Now, when you start a backup, you can specify the destination directory (as a Backup URL) using a local path on the destination machines. The backup agents will fetch data from the database and store it locally on the destination machines. + +Backup URLs +=========== + +Backup and Restore locations are specified by Backup URLs. Currently there are two valid Backup URL formats. + +Note that items in angle brackets (< and >) are just placeholders and must be replaced (including the brackets) with meaningful values. Items within square brackets ([ and ]) are optional. + +For local directories, the Backup URL format is + +:: + + file:// + +An example would be ``file:///home/backups`` which would refer to the directory ``/home/backups``. +Note that since paths must be absolute this will result in three slashes (/) in a row in the URL. + +Note that for local directory URLs the actual backup files will not be written to directly but rather to a uniquely timestamped subdirectory. When starting a restore the path to the timestamped subdirectory must be specified. + +For blob store backup locations, the Backup URL format is + +:: + + blobstore://[:]@[:]/[?=[&=]...] + + - API key to use for authentication + - API key's secret. Optional. + - Remote hostname or IP address to connect to + - Remote port to connect to. Optional. Default is 80. + - Name of backup. It can contain '/' characters, to place backups into a folder-like structure. + + = - Optional URL parameters. See below for details. + +If is not specified, it will be looked up in :ref:`blob credential sources`. + +An example blob store Backup URL would be ``blobstore://myKey:mySecret@something.domain.com:80/dec_1_2017_0400``. + +Blob store Backup URLs can have optional parameters at the end which set various limits on interactions with the blob store. All values must be positive decimal integers. The default values are not very restrictive. The most likely parameter a user would want to change is ``max_send_bytes_per_second`` (or ``sbps`` for short) which determines the upload speed to the blob service. + +Here is a complete list of valid parameters: + + *secure_connection* (or *sc*) - Set 1 for secure connection and 0 for unsecure connection. Defaults to secure connection. + + *connect_tries* (or *ct*) - Number of times to try to connect for each request. + + *request_tries* (or *rt*) - Number of times to try each request until a parseable HTTP response other than 429 is received. + + *requests_per_second* (or *rps*) - Max number of requests to start per second. + + *concurrent_requests* (or *cr*) - Max number of requests in progress at once. + + *multipart_max_part_size* (or *maxps*) - Max part size for multipart uploads. + + *multipart_min_part_size* (or *minps*) - Min part size for multipart uploads. + + *concurrent_uploads* (or *cu*) - Max concurrent uploads (part or whole) that can be in progress at once. + + *concurrent_reads_per_file* (or *crps*) - Max concurrent reads in progress for any one file. + + *read_block_size* (or *rbs*) - Block size in bytes to be used for reads. + + *read_ahead_blocks* (or *rab*) - Number of blocks to read ahead of requested offset. + + *read_cache_blocks_per_file* (or *rcb*) - Size of the read cache for a file in blocks. + + *max_send_bytes_per_second* (or *sbps*) - Max send bytes per second for all requests combined. + + *max_recv_bytes_per_second* (or *rbps*) - Max receive bytes per second for all requests combined + +.. _blob-credential-files: + +Blob Credential Files +============================== + +In order to help safeguard blob store credentials, the can optionally be omitted from blobstore:// URLs on the command line. Omitted secrets will be resolved at connect time using 1 or more Blob Credential files. + +Blob Credential files can be specified on the command line (via --blob_credentials ) or via the environment variable FDB_BLOB_CREDENTIALS which can be set to a colon-separated list of files. The command line takes priority over the environment variable however all files from both sources will be used. + +At connect time, the specified files are read in order and the first matching account specification (user@host) +will be used to obtain the secret key. + +The Blob Credential File format is JSON with the following schema: + +:: + + { + "accounts" : { + "user@host" : { "secret" : "SECRETKEY" }, + "user2@host2" : { "secret" : "SECRETKEY2" } + } + } + +SSL Support +=========== + +By default, backup will communicate over https. To configure https, the following environment variables are used: + +============================ ==================================================== +Environment Variable Purpose +============================ ==================================================== +``FDB_TLS_PLUGIN`` Path to the file to be loaded as the TLS plugin +``FDB_TLS_CERTIFICATE_FILE`` Path to the file from which the local certificates + can be loaded, used by the plugin +``FDB_TLS_KEY_FILE`` Path to the file from which to load the private + key, used by the plugin +``FDB_TLS_PASSWORD`` The byte-string representing the passcode for + unencrypting the private key +``FDB_TLS_CA_FILE`` Path to the file containing the CA certificates + to trust. Specify to override the default openssl + location. +============================ ==================================================== + + +``fdbbackup`` command line tool +=============================== + +.. program:: fdbbackup + +The ``fdbbackup`` command line tool is used to control backup jobs or to manage backup data. + +:: + + user@host$ fdbbackup [-h] + +The following options apply to most subcommands: + +``-C `` + Path to the cluster file that should be used to connect to the FoundationDB cluster you want to use. If not specified, a :ref:`default cluster file ` will be used. + +``-d `` + The Backup URL which the subcommand should read, write, or modify. For ``start`` operations, the Backup URL must be accessible by the ``backup_agent`` processes. + +``-t `` + A "tag" is a named slot in which a backup task executes. Backups on different named tags make progress and are controlled independently, though their executions are handled by the same set of backup agent processes. Any number of unique backup tags can be active at once. It the tag is not specified, the default tag name "default" is used. + +``--blob_credentials `` + Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. + +.. _backup-start: + +.. program:: fdbbackup start + +``start`` +--------- + +The ``start`` subcommand is used to start a backup. If there is already a backup in progress, the command will fail and the current backup will be unaffected. Otherwise, a backup is started. If the wait option is used, the command will wait for the backup to complete; otherwise, it returns immediately. + +:: + + user@host$ fdbbackup start [-t ] -d [-z] [-s ] [-w] [-k '[ ]']... + +``-z`` + Perform the backup continuously rather than terminating once a restorable backup is achieved. Database mutations within the backup's target key ranges will be continuously written to the backup as well as repeated inconsistent snapshots at the configured snapshot rate. + +``-s `` or ``--snapshot_interval `` + Specifies the duration, in seconds, of the inconsistent snapshots written to the backup in continuous mode. The default is 864000 which is 10 days. + +``-w`` + Wait for the backup to complete with behavior identical to that of the :ref:`wait command `. + +``-k '[ ]'`` + Specify a key range to be included in the backup. Can be used multiple times to specify multiple key ranges. The argument should be a single string containing either a BEGIN alone or both a BEGIN and END separated by a space. If only the BEGIN is specified, the END is assumed to be BEGIN + '\xff'. If no key ranges are different, the default is all user keys ('' to '\xff'). + + Each key range should be quoted in a manner appropriate for your command line environment. Here are some examples for Bash: :: + + user@host$ fdbbackup start -k 'apple bananna' -k 'mango pineapple' -d + user@host$ fdbbackup start -k '@pp1e b*n*nn*' -k '#an&0 p^n3app!e' -d + +.. program:: fdbbackup abort + +``abort`` +--------- + +The ``abort`` subcommand is used to abort a backup that is currently in progress. If there is no backup in progress, the command will return an error. The destination backup is NOT deleted automatically, and it may or may not be restorable depending on when the abort is done. + +:: + + user@host$ fdbbackup abort [-t ] + +.. program:: fdbbackup discontinue + +``discontinue`` +--------------- + +The ``discontinue`` subcommand is only available for backups that were started with the continuous (``-z``) option. Its effect is to discontinue the continuous backup. Note that the subcommand does *not* abort the backup; it simply allows the backup to complete as a noncontinuous backup would. + +:: + + user@host$ fdbbackup discontinue [-t ] [-w] + +``-w`` + Wait for the backup to complete with behavior identical to that of the :ref:`wait command `. + +.. _backup-wait: + +.. program:: fdbbackup wait + +``wait`` +-------- + +The ``wait`` subcommand is used to wait for a backup to complete, which is useful for scripting purposes. If there is a backup in progress, it waits for it to complete or be aborted and returns a status based on the result of the backup. If there is no backup in progress, it returns immediately based on the result of the previous backup. The exit code is zero (success) if the backup was completed successfully and nonzero if it was aborted. + +:: + + user@host$ fdbbackup wait [-t ] + +.. program:: fdbbackup status + +``status`` +---------- + +The ``status`` subcommand is used to get information on the current status of backup. It will show several backup metrics as well as recent errors which organized by whether or not they appear to be preventing backup progress. + +:: + + user@host$ fdbbackup status [-t ] + +.. program:: fdbbackup delete + +``delete`` +---------- + +The ``delete`` subcommand will delete the specified backup. + +:: + + user@host$ fdbbackup delete -d + +.. warning:: If you cancel a delete operation while it is in progress the specified backup is in an unknown state and is likely no longer usable. Repeat the delete command to finish deleting the backup. + +.. program:: fdbbackup expire + +``expire`` +---------- + +The ``expire`` subcommand will remove data from a backup prior to some point in time referred to as the 'cutoff'. + +:: + + user@host$ fdbbackup expire -d [] [--force] + +The expiration CUTOFF must be specified by one of the two following arguments: + + ``--expire_before_timestamp `` + Specifies the expiration cutoff to DATETIME. Requires a cluster file and will use version/timestamp metadata in the database to convert DATETIME to a database commit version. DATETIME must be in the form "YYYY-MM-DD.HH:MI:SS" in UTC. + + ``--expire_before_version `` + Specifies the cutoff by a database commit version. + +Optionally, the user can specify a minimum RESTORABILITY guarantee with one of the following options. + + ``--restorable_after_timestamp `` + Specifies that the backup must be restorable to DATETIME and later. Requires a cluster file and will use version/timestamp metadata in the database to convert DATETIME to a database commit version. DATETIME must be in the form "YYYY-MM-DD.HH:MI:SS" in UTC. + + ``--restorable_after_version `` + Specifies that the backup must be restorable as of VERSION and later. + +``-f`` or ``--force`` + If the designated cutoff will result in removal of data such that the backup's restorability would be reduced to either unrestorable or less restorable than the optional restorability requirement then the --force option must be given or the result will be an error and no action will be taken. + +.. program:: fdbbackup describe + +``describe`` +---------- + +The ``describe`` subcommand will analyze the given backup and print a summary of the snapshot and mutation data versions it contains as well as the version range of restorability the backup can currently provide. + +:: + + user@host$ fdbbackup describe -d [--version_timestamps] [-C ] + +``--version_timestamps`` + If the originating cluster is still available and is passed on the command line, this option can be specified in order for all versions in the output to also be converted to timestamps for better human readability. + + +.. program:: fdbbackup list + +``list`` +---------- + +The ``list`` subcommand will list the backups at a given 'base' or shortened Backup URL. + +:: + + user@host$ fdbbackup list -b + +``-b `` or ``--base_url `` + This a shortened Backup URL which looks just like a Backup URL but without the backup name so that the list command will discover and list all of the backups under that base URL. + + +``fdbrestore`` command line tool +================================ + +.. program:: fdbrestore + +The ``fdbrestore`` command line tool is used to control restore tasks. Note that a restore operation will not clear the target key ranges, for safety reasons, so you must manually clear the ranges to be restored prior to starting the restore. + +.. warning:: It is your responsibility to ensure that no clients are accessing the database while it is being restored. During the restore process the database is in an inconsistent state, and writes that happen during the restore process might be partially or completely overwritten by restored data. + +:: + + user@host$ fdbrestore (start | abort | wait | status) [OPTIONS] + +The following options apply to all commands: + +``-h`` + Get help on the ``fdbrestore`` command. + +``-t `` + Specify the tag for the restore task. Multiple restore tasks can be in progress at once so long as each task uses a different tag. The default tag is "default". + + .. warning:: If multiple restore tasks are in progress they should be restoring to different prefixes or the result is undefined. + +``-C `` + Path to the cluster file that should be used to connect to the FoundationDB cluster you want to use. If not specified, a :ref:`default cluster file ` will be used. + +``--blob_credentials `` + Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. + +.. _restore-start: + +``start`` +--------- + +The ``start`` command will start a new restore on the specified (or default) tag. The command will fail if a tag is already in use by an active restore. + +:: + user@host$ fdbrestore start -r [OPTIONS] + +``-r `` + Required. Specifies the Backup URL for the source backup data to restore to the database. The source data must be accessible by the ``backup_agent`` processes for the cluster. + +``-w`` + Wait for the restore to reach a final state (such as complete) before exiting. Prints a progress update every few seconds. Behavior is identical to that of the wait command. + +``-k `` + Specify list of key ranges from the backup to restore to the database + +``--remove_prefix `` + Remove PREFIX from the keys read from the backup + +``--add_prefix `` + Add PREFIX to restored keys before writing them to the database + +``-n`` + Perform a trial run without actually restoring any data. + +``-v `` + Instead of the latest version the backup can be restored to, restore to VERSION. + +.. program:: fdbrestore abort + +``abort`` +--------- + +The ``abort`` command will stop an active backup on the specified (or default) tag. It will display the final state of the restore tag. + +``wait`` +-------- + +The ``wait`` command will wait for the restore on the specified (or default) tag to reach a final state (such as complete or abort) and then exit. While waiting it will prints a progress update every few seconds. + +.. program:: fdbrestore status + +``status`` +---------- + +The ``status`` command will print a detailed status report on restore job progress. If a tag is specified, it will only show status for that specific tag, otherwise status for all tags will be shown. + +``backup_agent`` command line tool +================================== + +.. program:: backup_agent + +``backup_agent`` is started automatically on each server in the default configuration of FoundationDB, so you will not normally need to invoke it at the command line. One case in which you would need to do so would be to perform a backup to a destination which is not accessible via a shared filesystem. :: + + user@host$ backup_agent [-h] [-v] [-C ] + +``-h`` + Get help on the ``backup_agent`` command. + +``-v`` + Get the version of FoundationDB in use. + +``-C `` + Specify the path to the ``fdb.cluster`` file that should be used to connect to the FoundationDB cluster you want to back up. + + If not specified, a :ref:`default cluster file ` will be used. + +``--blob_credentials `` + Use FILE as a :ref:`Blob Credential File`. Can be used multiple times. + +.. _fdbdr-intro: + +``fdbdr`` command line tool +=========================== + +.. program:: fdbdr + +The ``fdbdr`` command line tool is used to manage DR tasks. + +:: + + user@host$ fdbdr [-h] [] -d -s [-v] + +The following arguments are used by multiple subcommands: + +``-h`` + Get help on the ``fdbdr`` command. + +``-v`` + Get the version of FoundationDB in use. + +``-d `` + Specify the path to the ``fdb.cluster`` file for the destination cluster of the DR operation. + +``-s `` + Specify the path to the ``fdb.cluster`` file for the source cluster of the DR operation. + +.. _dr-start: + +.. program:: fdbdr start + +``start`` +--------- + +The ``start`` subcommand is used to start a DR backup. If there is already a DR backup in progress, the command will fail and the current DR backup will be unaffected. + +``-k '[ ]'`` + Specify a key range to be included in the DR. Can be used multiple times to specify multiple key ranges. The argument should be a single string containing either a BEGIN alone or both a BEGIN and END separated by a space. If only the BEGIN is specified, the END is assumed to be BEGIN + '\xff'. If no key ranges are different, the default is all user keys ('' to '\xff'). + +.. program:: fdbdr switch + +``switch`` +---------- + +The ``switch`` subcommand is used to swap the source and destination database clusters of an active DR in differential mode. This means the destination will be unlocked and start streaming data into the source database, which will subsequently be locked. + +This command requires both databases to be available. While the switch command is working, both databases will be locked for a few seconds. + +.. program:: fdbdr abort + +``abort`` +--------- + +The ``abort`` subcommand is used to abort a DR that is currently in progress. If there is no backup in progress, the command will return an error. If the DR had already reached differential status, the abort command will leave the destination database at consistent snapshot of the source database from sometime in the past. + + .. warning:: The ``abort`` command will lose some amount of prior commits. + +.. program:: fdbdr status + +``status`` +---------- + +The ``status`` subcommand is used to get information on the current status of DR backup. It tells whether or not there is a DR in progress and whether or not there are active DR agents. It will also report any errors that have been encountered by the DR agents. + +``-e `` + Print the last (up to) ```` errors that were logged into the database by backup agents. The default is 10. + +``dr_agent`` command line tool +============================== + +.. program:: dr_agent + +Unlike ``backup_agent``, ``dr_agent`` is not started automatically in a default FoundationDB installation. A ``dr_agent`` needs the cluster files for both the source database and the destination database, and can only perform a backup in one direction (from source to destination) at a time. + +:: + + user@host$ dr_agent [-h] [-v] -d -s + +``-h`` + Get help on the ``fdbdr`` command. + +``-v`` + Get the version of FoundationDB in use. + +``-d `` + Specify the path to the ``fdb.cluster`` file for the destination cluster of the DR operation. + +``-s `` + Specify the path to the ``fdb.cluster`` file for the source cluster of the DR operation. diff --git a/documentation/sphinx/source/building-cluster.rst b/documentation/sphinx/source/building-cluster.rst index c89e2a614aa..0f72fd9443c 100644 --- a/documentation/sphinx/source/building-cluster.rst +++ b/documentation/sphinx/source/building-cluster.rst @@ -148,7 +148,7 @@ There is also a convenience option, ``coordinators auto``, that will automatical .. note:: |coordinators-auto| -You can also change the cluster ``description``, as decribed in :ref:`configuration-setting-cluster-description`. +You can also change the cluster ``description``, as described in :ref:`configuration-setting-cluster-description`. Next steps ========== diff --git a/documentation/sphinx/source/class-scheduling-go.rst b/documentation/sphinx/source/class-scheduling-go.rst index e0781649c9b..6457fb73523 100644 --- a/documentation/sphinx/source/class-scheduling-go.rst +++ b/documentation/sphinx/source/class-scheduling-go.rst @@ -29,7 +29,7 @@ Before using the API, we need to specify the API version. This allows programs t .. code-block:: go - fdb.MustAPIVersion(510) + fdb.MustAPIVersion(520) Next, we open a FoundationDB database. The API will connect to the FoundationDB cluster indicated by the :ref:`default cluster file `. @@ -78,7 +78,7 @@ If this is all working, it looks like we are ready to start building a real appl func main() { // Different API versions may expose different runtime behaviors. - fdb.MustAPIVersion(510) + fdb.MustAPIVersion(520) // Open the default database from the system cluster db := fdb.MustOpenDefault() @@ -149,8 +149,8 @@ FoundationDB includes a few modules that make it easy to model data using this a The :mod:`directory` module lets us open a :ref:`directory ` in the database:: schedulingDir, err := directory.CreateOrOpen(db, []string{"scheduling"}, nil) - if e != nil { - log.Fatal(e) + if err != nil { + log.Fatal(err) } The :func:`CreateOrOpen` function returns a :ref:`subspace ` where we'll store our application data. Each subspace has a fixed prefix it uses when defining keys. The prefix corresponds to the first element of a tuple. We decided that we wanted ``"attends"`` and ``"class"`` as our prefixes, so we'll create new subspaces for them within the ``scheduling`` subspace.:: @@ -661,7 +661,7 @@ Here's the code for the scheduling tutorial: } func main() { - fdb.MustAPIVersion(510) + fdb.MustAPIVersion(520) db := fdb.MustOpenDefault() schedulingDir, err := directory.CreateOrOpen(db, []string{"scheduling"}, nil) diff --git a/documentation/sphinx/source/class-scheduling-java.rst b/documentation/sphinx/source/class-scheduling-java.rst index da74df06b17..ed6d962bbb3 100644 --- a/documentation/sphinx/source/class-scheduling-java.rst +++ b/documentation/sphinx/source/class-scheduling-java.rst @@ -30,7 +30,7 @@ Before using the API, we need to specify the API version. This allows programs t private static final Database db; static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); } @@ -66,7 +66,7 @@ If this is all working, it looks like we are ready to start building a real appl private static final Database db; static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); } @@ -436,7 +436,7 @@ Here's the code for the scheduling tutorial: private static final Database db; static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); } diff --git a/documentation/sphinx/source/class-scheduling-ruby.rst b/documentation/sphinx/source/class-scheduling-ruby.rst index b782c04db1b..a3a6677e315 100644 --- a/documentation/sphinx/source/class-scheduling-ruby.rst +++ b/documentation/sphinx/source/class-scheduling-ruby.rst @@ -23,7 +23,7 @@ Open a Ruby interactive interpreter and import the FoundationDB API module:: Before using the API, we need to specify the API version. This allows programs to maintain compatibility even if the API is modified in future versions:: - > FDB.api_version 510 + > FDB.api_version 520 => nil Next, we open a FoundationDB database. The API will connect to the FoundationDB cluster indicated by the :ref:`default cluster file `. :: @@ -46,7 +46,7 @@ If this is all working, it looks like we are ready to start building a real appl .. code-block:: ruby require 'fdb' - FDB.api_version 510 + FDB.api_version 520 @db = FDB.open @db['hello'] = 'world' print 'hello ', @db['hello'] @@ -368,7 +368,7 @@ Here's the code for the scheduling tutorial: require 'fdb' - FDB.api_version 510 + FDB.api_version 520 #################################### ## Initialization ## diff --git a/documentation/sphinx/source/class-scheduling.rst b/documentation/sphinx/source/class-scheduling.rst index d9efa45716e..0be06575444 100644 --- a/documentation/sphinx/source/class-scheduling.rst +++ b/documentation/sphinx/source/class-scheduling.rst @@ -30,7 +30,7 @@ Open a Python interactive interpreter and import the FoundationDB API module:: Before using the API, we need to specify the API version. This allows programs to maintain compatibility even if the API is modified in future versions:: - >>> fdb.api_version(510) + >>> fdb.api_version(520) Next, we open a FoundationDB database. The API will connect to the FoundationDB cluster indicated by the :ref:`default cluster file `. :: @@ -38,20 +38,20 @@ Next, we open a FoundationDB database. The API will connect to the FoundationDB We are ready to use the database. In Python, using the ``[]`` operator on the db object is a convenient syntax for performing a read or write on the database. First, let's simply write a key-value pair: - >>> db['hello'] = 'world' + >>> db[b'hello'] = b'world' When this command returns without exception, the modification is durably stored in FoundationDB! Under the covers, this function creates a transaction with a single modification. We'll see later how to do multiple operations in a single transaction. For now, let's read back the data:: - >>> print 'hello', db['hello'] + >>> print 'hello', db[b'hello'] hello world If this is all working, it looks like we are ready to start building a real application. For reference, here's the full code for "hello world":: import fdb - fdb.api_version(510) + fdb.api_version(520) db = fdb.open() - db['hello'] = 'world' - print 'hello', db['hello'] + db[b'hello'] = b'world' + print 'hello', db[b'hello'] Class scheduling application ============================ @@ -91,7 +91,7 @@ FoundationDB includes a few tools that make it easy to model data using this app opening a :ref:`directory ` in the database:: import fdb - fdb.api_version(510) + fdb.api_version(520) db = fdb.open() scheduling = fdb.directory.create_or_open(db, ('scheduling',)) @@ -170,7 +170,7 @@ Before students can do anything else, they need to be able to retrieve a list of def available_classes(tr): return [course.unpack(k)[0] for k, v in tr[course.range(())]] -In general, the :meth:`Subspace.range` method returns a Python ``slice`` representing all the key-value pairs starting with the specified tuple. In this case, we want all classes, so we call :meth:`course.range` with the empty tuple ``()``. FoundationDB's ``tr[slice]`` function returns an iterable list of key-values in the range specified by the slice. We unpack the key ``k`` and value ``v`` in a comprehension. To extract the class name itself, we unpack the key into a tuple using the :py:mod:`fdb.tuple` module and take its third part. (The first and second parts are the prefixes for the ``scheduling`` and ``course`` subspaces, respectively.) +In general, the :meth:`Subspace.range` method returns a Python ``slice`` representing all the key-value pairs starting with the specified tuple. In this case, we want all classes, so we call :meth:`course.range` with the empty tuple ``()``. FoundationDB's ``tr[slice]`` function returns an iterable list of key-values in the range specified by the slice. We unpack the key ``k`` and value ``v`` in a comprehension. To extract the class name itself, we unpack the key into a tuple using the :meth:`Subspace.unpack` method and take the first field. (The first and second parts of the tuple, the ``scheduling`` and ``course`` subspace prefixes, are removed by the ``unpack`` hence the reason we take the first field of the tuple.) Signing up for a class ---------------------- @@ -232,6 +232,19 @@ This is easy -- we simply add a condition to check that the value is non-zero. L We now have to check that we aren't already signed up, since we don't want a double sign up to decrease the number of seats twice. Then we look up how many seats are left to make sure there is a seat remaining so we don't push the counter into the negative. If there is a seat remaining, we decrement the counter. +Similarly, the ``drop`` function is modified as follows: + +.. code-block:: python + :emphasize-lines: 4,5 + + @fdb.transactional + def drop(tr, s, c): + rec = attends.pack((s, c)) + if not tr[rec].present(): return # not taking this class + tr[course.pack((c,))] = fdb.tuple.pack((fdb.tuple.unpack(tr[course.pack((c,))])[0] + 1,)) + del tr[rec] + +Once again we check to see if the student is signed up and if not, we can just return as we don't want to incorrectly increase the number of seats. We then adjust the number of seats by one by taking the current value, incrementing it by one, and then storing back. Concurrency and consistency --------------------------- @@ -247,23 +260,6 @@ Idempotence Occasionally, a transaction might be retried even after it succeeds (for example, if the client loses contact with the cluster at just the wrong moment). This can cause problems if transactions are not written to be idempotent, i.e. to have the same effect if committed twice as if committed once. There are generic design patterns for :ref:`making any transaction idempotent `, but many transactions are naturally idempotent. For example, all of the transactions in this tutorial are idempotent. -Dropping with limited seats ---------------------------- - -Let's finish up the limited seats feature by modifying the drop function: - -.. code-block:: python - :emphasize-lines: 4,5 - - @fdb.transactional - def drop(tr, s, c): - rec = attends.pack((s, c)) - if not tr[rec].present(): return # not taking this class - tr[course.pack((c,))] = fdb.tuple.pack((fdb.tuple.unpack(tr[course.pack((c,))])[0] + 1,)) - del tr[rec] - -This case is easier than signup because there are no constraints we can hit. We just need to make sure the student is in the class and to "give back" one seat when the student drops. - More features?! --------------- @@ -336,7 +332,7 @@ Here's the code for the scheduling tutorial:: import fdb import fdb.tuple - fdb.api_version(510) + fdb.api_version(520) #################################### diff --git a/documentation/sphinx/source/command-line-interface.rst b/documentation/sphinx/source/command-line-interface.rst index b4805ca57ba..dd32ef85425 100644 --- a/documentation/sphinx/source/command-line-interface.rst +++ b/documentation/sphinx/source/command-line-interface.rst @@ -74,7 +74,7 @@ Redundancy modes define storage requirements, required cluster size, and resilie * ``three_data_hall`` * ``three_datacenter`` -For descriptions of redundacy modes, see :ref:`configuration-choosing-redundancy-mode`. +For descriptions of redundancy modes, see :ref:`configuration-choosing-redundancy-mode`. storage engine ^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ The ``coordinators`` command is used to change cluster coordinators or descripti Addresses may be specified as a list of IP:port pairs (such as ``coordinators 10.0.0.1:4000 10.0.0.2:4000 10.0.0.3:4000``). If addresses are specified, the coordinators will be set to them. An ``fdbserver`` process must be running on each of the specified addresses. -If ``auto`` is specified, coordinator addresses will be choosen automatically to support the configured redundancy level. (If the current set of coordinators are healthy and already support the configured redundancy level, nothing will be changed.) +If ``auto`` is specified, coordinator addresses will be chosen automatically to support the configured redundancy level. (If the current set of coordinators are healthy and already support the configured redundancy level, nothing will be changed.) For more information on setting coordinators, see :ref:`configuration-changing-coordination-servers`. @@ -194,7 +194,7 @@ The following options are available for use with the ``option`` command: ``READ_AHEAD_DISABLE`` - Disables read-ahead caching for range reads. Under normal operation, a transaction will read extra rows from the database into cache if range reads are used to page through a series of data one row at a time (i.e. if a range read with a one row limit is followed by another one row range read starting immediately after the result of the first). -``READ_YOUR_WRITES_DISABLE`` - Reads performed by a transaction will not see any prior mutations that occured in that transaction, instead seeing the value which was in the database at the transaction's read version. This option may provide a small performance benefit for the client, but also disables a number of client-side optimizations which are beneficial for transactions which tend to read and write the same keys within a single transaction. +``READ_YOUR_WRITES_DISABLE`` - Reads performed by a transaction will not see any prior mutations that occurred in that transaction, instead seeing the value which was in the database at the transaction's read version. This option may provide a small performance benefit for the client, but also disables a number of client-side optimizations which are beneficial for transactions which tend to read and write the same keys within a single transaction. ``RETRY_LIMIT`` - Set a maximum number of retries after which additional calls to ``onError`` will throw the most recently seen error code. Valid parameter values are ``[-1, INT_MAX]``. If set to -1, will disable the retry limit. Like all transaction options, the retry limit must be reset after a call to ``onError``. This behavior allows the user to make the retry limit dynamic. diff --git a/documentation/sphinx/source/configuration.rst b/documentation/sphinx/source/configuration.rst index c05f7d2ed9c..a85f9fd46c8 100644 --- a/documentation/sphinx/source/configuration.rst +++ b/documentation/sphinx/source/configuration.rst @@ -71,7 +71,7 @@ You can change coordinators when the following conditions are met: * a majority of the current coordinators are available; * all of the new coordinators are available; and -* client and server cluster files are writable. +* client and server cluster files and their parent directories are writable. ``fdbcli`` supports a ``coordinators`` command to specify the new list of coordinators:: @@ -257,7 +257,7 @@ Contains default parameters for all fdbserver processes on this machine. These s * ``maxlogssize``: Delete the oldest log file when the total size of all log files exceeds the specified size. If set to 0B, old log files will not be deleted. The default value is 100MiB. * ``class``: Process class specifying the roles that will be taken in the cluster. Recommended options are ``storage``, ``transaction``, ``stateless``. See :ref:`guidelines-process-class-config` for process class config recommendations. * ``memory``: Maximum memory used by the process. The default value is 8GiB. When specified without a unit, MiB is assumed. This parameter does not change the memory allocation of the program. Rather, it sets a hard limit beyond which the process will kill itself and be restarted. The default value of 8GiB is double the intended memory usage in the default configuration (providing an emergency buffer to deal with memory leaks or similar problems). It is *not* recommended to decrease the value of this parameter below its default value. It may be *increased* if you wish to allocate a very large amount of storage engine memory or cache. In particular, when the ``storage_memory`` parameter is increased, the ``memory`` parameter should be increased by an equal amount. -* ``storage_memory``: Maximum memory used for data storage. This paramenter is used *only* with memory storage engine, not the ssd storage engine. The default value is 1GiB. When specified without a unit, MB is assumed. Clusters will be restricted to using this amount of memory per process for purposes of data storage. Memory overhead associated with storing the data is counted against this total. If you increase the ``storage_memory``, you should also increase the ``memory`` parameter by the same amount. +* ``storage_memory``: Maximum memory used for data storage. This parameter is used *only* with memory storage engine, not the ssd storage engine. The default value is 1GiB. When specified without a unit, MB is assumed. Clusters will be restricted to using this amount of memory per process for purposes of data storage. Memory overhead associated with storing the data is counted against this total. If you increase the ``storage_memory``, you should also increase the ``memory`` parameter by the same amount. * ``locality_machineid``: Machine identifier key. All processes on a machine should share a unique id. By default, processes on a machine determine a unique id to share. This does not generally need to be set. * ``locality_zoneid``: Zone identifier key. Processes that share a zone id are considered non-unique for the purposes of data replication. If unset, defaults to machine id. * ``locality_dcid``: Data center identifier key. All processes physically located in a data center should share the id. No default value. If you are depending on data center based replication this must be set on all processes. diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index 10375cad86a..efcf873522f 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-5.1.7.pkg `_ +* `FoundationDB-5.2.8.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-5.1.7-1_amd64.deb `_ -* `foundationdb-server-5.1.7-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-5.2.8-1_amd64.deb `_ +* `foundationdb-server-5.2.8-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-5.1.7-1.el6.x86_64.rpm `_ -* `foundationdb-server-5.1.7-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-5.2.8-1.el6.x86_64.rpm `_ +* `foundationdb-server-5.2.8-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-5.1.7-1.el7.x86_64.rpm `_ -* `foundationdb-server-5.1.7-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-5.2.8-1.el7.x86_64.rpm `_ +* `foundationdb-server-5.2.8-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-5.1.7-x64.msi `_ +* `foundationdb-5.2.8-x64.msi `_ API Language Bindings ===================== @@ -51,25 +51,25 @@ C FoundationDB's C bindings are installed with the FoundationDB client binaries. You can find more details in the :doc:`C API Documentation `. -Python 2.7 - 3.4 +Python 2.7 - 3.5 ---------------- On macOS and Windows, the FoundationDB Python API bindings are installed as part of your FoundationDB installation. If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-5.1.7.tar.gz `_ +* `foundationdb-5.2.8.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-5.1.7.gem `_ +* `fdb-5.2.8.gem `_ Java 8+ ------- -* `fdb-java-5.1.7.jar `_ -* `fdb-java-5.1.7-javadoc.jar `_ +* `fdb-java-5.2.8.jar `_ +* `fdb-java-5.2.8-javadoc.jar `_ Go 1.1+ ------- diff --git a/documentation/sphinx/source/hierarchical-documents-java.rst b/documentation/sphinx/source/hierarchical-documents-java.rst index 78dfd05ad4e..233be15cac6 100644 --- a/documentation/sphinx/source/hierarchical-documents-java.rst +++ b/documentation/sphinx/source/hierarchical-documents-java.rst @@ -67,13 +67,13 @@ Here’s a basic implementation of the recipe. private static final Subspace docSpace; private static final long EMPTY_OBJECT = -2; private static final long EMPTY_ARRAY = -1; - + static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); docSpace = new Subspace(Tuple.from("D")); } - + @SuppressWarnings("unchecked") private static ArrayList toTuplesSwitch(Object o){ if(o instanceof ArrayList){ @@ -84,7 +84,7 @@ Here’s a basic implementation of the recipe. return toTuples(o); } } - + private static ArrayList toTuples(ArrayList item){ if(item.isEmpty()){ ArrayList val = new ArrayList(); @@ -100,7 +100,7 @@ Here’s a basic implementation of the recipe. return val; } } - + private static ArrayList toTuples(Map item){ if(item.isEmpty()){ ArrayList val = new ArrayList(); @@ -116,13 +116,13 @@ Here’s a basic implementation of the recipe. return val; } } - + private static ArrayList toTuples(Object item){ ArrayList val = new ArrayList(); val.add(Tuple.from(item)); return val; } - + private static ArrayList getTruncated(ArrayList vals){ ArrayList list = new ArrayList(); for(Tuple val : vals){ @@ -130,26 +130,26 @@ Here’s a basic implementation of the recipe. } return list; } - + private static Object fromTuples(ArrayList tuples){ if(tuples == null){ return null; } - + Tuple first = tuples.get(0); // Determine kind of object from // first tuple. if(first.size() == 1){ return first.get(0); // Primitive type. } - + if(first.equals(Tuple.from(EMPTY_OBJECT, null))){ return new HashMap(); // Empty map. } - + if(first.equals(Tuple.from(EMPTY_ARRAY))){ return new ArrayList(); // Empty list. } - + HashMap> groups = new HashMap>(); for(Tuple t : tuples){ if(groups.containsKey(t.get(0))){ @@ -160,7 +160,7 @@ Here’s a basic implementation of the recipe. groups.put(t.get(0),list); } } - + if(first.get(0).equals(0l)){ // Array. ArrayList array = new ArrayList(); @@ -184,17 +184,17 @@ Here’s a basic implementation of the recipe. doc.put("doc_id", getNewID(tr)); } for(Tuple t : toTuples(doc)){ - tr.set(docSpace.pack(Tuple.from(doc.get("doc_id")).addAll(t.popBack())), + tr.set(docSpace.pack(Tuple.from(doc.get("doc_id")).addAll(t.popBack())), Tuple.from(t.get(t.size() - 1)).pack()); } return doc.get("doc_id"); }); } - + public static Object getDoc(TransactionContext tcx, final Object ID){ return getDoc(tcx, ID, Tuple.from()); } - + public static Object getDoc(TransactionContext tcx, final Object ID, final Tuple prefix){ return tcx.run(tr -> { Future v = tr.get(docSpace.pack(Tuple.from(ID).addAll(prefix))); @@ -213,7 +213,7 @@ Here’s a basic implementation of the recipe. } }); } - + private static int getNewID(TransactionContext tcx){ return tcx.run(tr -> { boolean found = false; diff --git a/documentation/sphinx/source/images/Architecture.png b/documentation/sphinx/source/images/Architecture.png new file mode 100644 index 00000000000..5cae25eb4fc Binary files /dev/null and b/documentation/sphinx/source/images/Architecture.png differ diff --git a/documentation/sphinx/source/index.rst b/documentation/sphinx/source/index.rst index af11c6dd9e3..da6b1b89010 100644 --- a/documentation/sphinx/source/index.rst +++ b/documentation/sphinx/source/index.rst @@ -50,4 +50,5 @@ The latest changes are detailed in :doc:`release-notes`. The documentation has t design-recipes api-reference tutorials + administration earlier-release-notes diff --git a/documentation/sphinx/source/moving-a-cluster.rst b/documentation/sphinx/source/moving-a-cluster.rst index 9e64fbaedd0..56cbcc27e0e 100644 --- a/documentation/sphinx/source/moving-a-cluster.rst +++ b/documentation/sphinx/source/moving-a-cluster.rst @@ -40,6 +40,6 @@ $ sudo service foundationdb stop fdb> exclude 192.168.1.1:4500 192.168.1.2:4500 192.168.1.3:4500 -8. Run ``coordinators auto`` in ``fdbcli`` to move coordination state to the new machines. Please note that this will cause the fdb.cluster file to be updated with the addresses of the new machines. Any currently connected clients will be notified and (assuming they have appropriate file system permissions) will update their own copy of the cluster file. As long as the original machines are still running, any clients that connect to them will be automatically forwarded to the new cluster coordinators. However, if you have a client that has not yet connected or only connects intermittently, you will need to copy the new cluster file from one of the new machines to the client machine. +8. Run ``coordinators auto`` in ``fdbcli`` to move coordination state to the new machines. Please note that this will cause the fdb.cluster file to be updated with the addresses of the new machines. Any currently connected clients will be notified and (assuming they have appropriate file system :ref:`permissions `) will update their own copy of the cluster file. As long as the original machines are still running, any clients that connect to them will be automatically forwarded to the new cluster coordinators. However, if you have a client that has not yet connected or only connects intermittently, you will need to copy the new cluster file from one of the new machines to the client machine. 9. The ``status details`` command in the fdbcli will now show only the new processes (both as workers and coordinators), and you can safely shut down the older machines. diff --git a/documentation/sphinx/source/mr-status.rst b/documentation/sphinx/source/mr-status.rst index 037e45936fd..97a6053e98b 100644 --- a/documentation/sphinx/source/mr-status.rst +++ b/documentation/sphinx/source/mr-status.rst @@ -186,7 +186,7 @@ The following format informally describes the JSON containing the status data. T "name": < "incorrect_cluster_file_contents" | ... >, - "description": "Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." + "description": "Cluster file contents do not match current cluster connection string. Verify the cluster file and its parent directory are writable and that the cluster file has not been overwritten externally." } ], "reasons": [ diff --git a/documentation/sphinx/source/multimaps-java.rst b/documentation/sphinx/source/multimaps-java.rst index 6de36c2364a..25623e6475d 100644 --- a/documentation/sphinx/source/multimaps-java.rst +++ b/documentation/sphinx/source/multimaps-java.rst @@ -41,7 +41,7 @@ Because we need to store multiple values per index, we'll store them within keys ByteBuffer b = ByteBuffer.allocate(8); b.order(ByteOrder.LITTLE_ENDIAN); - b.putLong(1l); + b.putLong(1l); tr.mutate(MutationType.ADD, key, b.array()); By using a read-free atomic addition, FoundationDB guarantees that the addition operation will not conflict. As a result, values can be frequently added by multiple clients. @@ -72,46 +72,46 @@ Here’s a simple implementation of multimaps with multisets as described: private static final Database db; private static final Subspace multi; private static final int N = 100; - + static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); multi = new Subspace(Tuple.from("M")); } - + private static void addHelp(TransactionContext tcx, final byte[] key, final long amount){ tcx.run(tr -> { ByteBuffer b = ByteBuffer.allocate(8); b.order(ByteOrder.LITTLE_ENDIAN); b.putLong(amount); - + tr.mutate(MutationType.ADD, key, b.array()); - + return null; }); } - + private static long getLong(byte[] val){ ByteBuffer b = ByteBuffer.allocate(8); b.order(ByteOrder.LITTLE_ENDIAN); b.put(val); return b.getLong(0); } - - public static void add(TransactionContext tcx, final String index, + + public static void add(TransactionContext tcx, final String index, final Object value){ tcx.run(tr -> { addHelp(tr, multi.subspace(Tuple.from(index,value)).getKey(),1l); return null; }); } - + public static void subtract(TransactionContext tcx, final String index, final Object value){ tcx.run(tr -> { Future v = tr.get(multi.subspace( Tuple.from(index,value)).getKey()); - + if(v.get() != null && getLong(v.get()) > 1l){ addHelp(tr, multi.subspace(Tuple.from(index,value)).getKey(), -1l); } else { @@ -120,7 +120,7 @@ Here’s a simple implementation of multimaps with multisets as described: return null; }); } - + public static ArrayList get(TransactionContext tcx, final String index){ return tcx.run(tr -> { ArrayList vals = new ArrayList(); @@ -131,20 +131,20 @@ Here’s a simple implementation of multimaps with multisets as described: return vals; }); } - - public static HashMap getCounts(TransactionContext tcx, + + public static HashMap getCounts(TransactionContext tcx, final String index){ return tcx.run(tr -> { HashMap vals = new HashMap(); for(KeyValue kv : tr.getRange(multi.subspace( Tuple.from(index)).range())){ - vals.put(multi.unpack(kv.getKey()).get(1), + vals.put(multi.unpack(kv.getKey()).get(1), getLong(kv.getValue())); } return vals; }); } - + public static boolean isElement(TransactionContext tcx, final String index, final Object value){ return tcx.run(tr -> { diff --git a/documentation/sphinx/source/old-release-notes/release-notes-014.rst b/documentation/sphinx/source/old-release-notes/release-notes-014.rst index 00aa296be44..e7c775c5959 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-014.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-014.rst @@ -1,77 +1,77 @@ -####################### -Release Notes (Alpha 5) -####################### - -FoundationDB Alpha 5 -==================== - -Language support -------------------------- - -* FoundationDB now supports :doc:`Ruby ` - -* FoundationDB now supports :doc:`Node.js ` - -* FoundationDB now supports `Java `_ and other JVM languages. - -.. _alpha-5-rel-notes-features: - -Features ------------- -* A new :doc:`backup ` system allows scheduled backups of a snapshot of the FoundationDB database to an external filesystem. - -* :doc:`Integrated HTML documentation ` - -* :ref:`Snapshot reads ` allow API clients to selectively relax FoundationDB's strong isolation guarantee. Appropriate use can of them can reduce :ref:`conflict-ranges` but makes reasoning about concurrency harder. - -* :ref:`Streaming modes ` allow API clients to adjust how FoundationDB transfers data for range reads for improved performance. - -* Client APIs automatically detect the appropriate network interface (local address) when connecting to a cluster, and will look for a :ref:`default-cluster-file`. - -Compatibility -------------- - -* Tuples encoded with prior alpha versions are incompatible with the tuple layer in Alpha 5. - -* Databases created with Alpha 4 will be compatible. (See :ref:`Upgrading from older versions ` for upgrade instructions) - -* Databases created before Alpha 4 will be incompatible. (See :ref:`Upgrading from older versions ` for details) - -Changes to all APIs -------------------- - -* The API version has been updated to 14. - -* :ref:`Snapshot reads ` (see :ref:`Features `, above). - -* :ref:`Streaming modes ` (see :ref:`Features `, above). - -* Automatic network interface detection (see :ref:`Features `, above). - -* The tuple layer supports unicode strings (encoded as UTF-8), has a more compact encoding, and is not compatible with data from prior versions. - -* Reversed range reads are now exposed through a separate parameter rather than via a negative ``limit``. - -* Extensible options are now exposed at the network, cluster, database and transaction levels. The parameters to :c:func:`fdb_setup_network` and :py:func:`fdb.init` have been replaced by network options. - -* Option enumerations are available in a machine-readable format for the benefit of third-party language binding developers. - -Python API changes ------------------- - -* :py:func:`fdb.open` can be called with no parameters to use the :ref:`default-cluster-file`. - -* Waiting on a Future object has changed from ``.get()`` to :py:meth:`.wait() ` - -* Reversed range reads can by specified by passing a slice object with a -1 step. - -* The convenience read methods on :py:class:`fdb.Database` are now transactional. - -C API changes -------------- - -* Byte limits exposed in :c:func:`fdb_transaction_get_range`. These are not currently exposed by any of the higher level clients (and usually streaming modes should be preferred). - -* :c:func:`fdb_future_get_keyvalue_array` returns an explicit flag indicating whether there is more data in the range beyond the limits passed to :c:func:`fdb_transaction_get_range`. - -* ``fdb_transaction_get_range_selector`` has been eliminated - :c:func:`fdb_transaction_get_range` always takes key selectors. +####################### +Release Notes (Alpha 5) +####################### + +FoundationDB Alpha 5 +==================== + +Language support +------------------------- + +* FoundationDB now supports :doc:`Ruby ` + +* FoundationDB now supports :doc:`Node.js ` + +* FoundationDB now supports `Java `_ and other JVM languages. + +.. _alpha-5-rel-notes-features: + +Features +------------ +* A new :doc:`backup ` system allows scheduled backups of a snapshot of the FoundationDB database to an external filesystem. + +* :doc:`Integrated HTML documentation ` + +* :ref:`Snapshot reads ` allow API clients to selectively relax FoundationDB's strong isolation guarantee. Appropriate use can of them can reduce :ref:`conflict-ranges` but makes reasoning about concurrency harder. + +* :ref:`Streaming modes ` allow API clients to adjust how FoundationDB transfers data for range reads for improved performance. + +* Client APIs automatically detect the appropriate network interface (local address) when connecting to a cluster, and will look for a :ref:`default-cluster-file`. + +Compatibility +------------- + +* Tuples encoded with prior alpha versions are incompatible with the tuple layer in Alpha 5. + +* Databases created with Alpha 4 will be compatible. (See :ref:`Upgrading from older versions ` for upgrade instructions) + +* Databases created before Alpha 4 will be incompatible. (See :ref:`Upgrading from older versions ` for details) + +Changes to all APIs +------------------- + +* The API version has been updated to 14. + +* :ref:`Snapshot reads ` (see :ref:`Features `, above). + +* :ref:`Streaming modes ` (see :ref:`Features `, above). + +* Automatic network interface detection (see :ref:`Features `, above). + +* The tuple layer supports unicode strings (encoded as UTF-8), has a more compact encoding, and is not compatible with data from prior versions. + +* Reversed range reads are now exposed through a separate parameter rather than via a negative ``limit``. + +* Extensible options are now exposed at the network, cluster, database and transaction levels. The parameters to :c:func:`fdb_setup_network` and :py:func:`fdb.init` have been replaced by network options. + +* Option enumerations are available in a machine-readable format for the benefit of third-party language binding developers. + +Python API changes +------------------ + +* :py:func:`fdb.open` can be called with no parameters to use the :ref:`default-cluster-file`. + +* Waiting on a Future object has changed from ``.get()`` to :py:meth:`.wait() ` + +* Reversed range reads can by specified by passing a slice object with a -1 step. + +* The convenience read methods on :py:class:`fdb.Database` are now transactional. + +C API changes +------------- + +* Byte limits exposed in :c:func:`fdb_transaction_get_range`. These are not currently exposed by any of the higher level clients (and usually streaming modes should be preferred). + +* :c:func:`fdb_future_get_keyvalue_array` returns an explicit flag indicating whether there is more data in the range beyond the limits passed to :c:func:`fdb_transaction_get_range`. + +* ``fdb_transaction_get_range_selector`` has been eliminated - :c:func:`fdb_transaction_get_range` always takes key selectors. diff --git a/documentation/sphinx/source/old-release-notes/release-notes-016.rst b/documentation/sphinx/source/old-release-notes/release-notes-016.rst index d414a6d477b..6fb4b0ea2ac 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-016.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-016.rst @@ -1,71 +1,71 @@ -####################### -Release Notes (Alpha 6) -####################### - -FoundationDB Alpha 6 -==================== - -Platform support -------------------------- - -* FoundationDB now supports both clients and development servers on :doc:`Mac OS X `. - -* FoundationDB now supports both clients and development servers on (64-bit) Windows. - -* All language APIs are supported on Linux, Mac, and Windows (except for Ruby on Windows, because there is not a 64-bit Ruby for Windows.) - -Features ------------- - -* The set of coordination servers can be safely :ref:`changed ` on-the-fly via the CLI. - -* Unintentional deletion of the coordination state files is now ACID-safe and self-correcting when a majority of the state files still exist. - -* The :ref:`foundationdb.conf ` file format has changed. - -* A new more flexible and automatic system for :ref:`network configuration `. Common server setups will auto-configure using the cluster file. More advanced setups are supported via separate configurable listen and public addresses. - -* The CLI now support tab-completion. - -* The CLI now supports setting transaction options - -* The CLI has a new command "getrangekeys" that returns the keys in a range and omits the values. - -* The database size estimate shown in the CLI status is much more accurate. - -Performance --------------- - -* Improved latency performance for intense workloads with range-read operations. - -* Improved performance and decreased memory usage for certain intense write workloads targeting a small set of keys (such as sequential insert). - -Fixes --------- - -* An incorrect result could be returned by a range read when: (1) The range start was specified using a non-default "less than" type key selector; and (2) the range read started at the beginning of the database; and (3) the transaction also included a prior write to a key less than the key of the begin key selector. - -* In certain cases a FoundationDB cluster would not correctly re-configure itself to achieve a more optimal usage of servers of specific machine classes. - -Changes to all APIs -------------------- - -* The API version has been updated from 14 to 16. (Thanks to our API versioning technology, programs requesting API version 14 will work unmodified.) - -* Calling the :py:meth:`reset ` method of a transaction now also resets transaction options. - -* :ref:`System keys ` (those beginning with the byte ``0xFF``) are now inaccessible by default. - -* Simpler network setup: The network connection options are no longer necessary and have been deprecated. - -* Three new transaction options (:py:meth:`READ_AHEAD_DISABLE `, :py:meth:`READ_YOUR_WRITES_DISABLE `, and :py:meth:`ACCESS_SYSTEM_KEYS `) enable more control for advanced applications. - -Changes to the Java API ------------------------- - -* A new construct `AsyncUtil.whileTrue() <../javadoc/com/apple/cie/foundationdb/async/AsyncUtil.html#whileTrue-com.apple.foundationdb.async.Function->`_ simplifies writing loops using the asynchronous version of the Java FDB client. - -Earlier release notes ---------------------- - -For changes in alpha 5, see :doc:`Release Notes (Alpha 5) `. +####################### +Release Notes (Alpha 6) +####################### + +FoundationDB Alpha 6 +==================== + +Platform support +------------------------- + +* FoundationDB now supports both clients and development servers on :doc:`Mac OS X `. + +* FoundationDB now supports both clients and development servers on (64-bit) Windows. + +* All language APIs are supported on Linux, Mac, and Windows (except for Ruby on Windows, because there is not a 64-bit Ruby for Windows.) + +Features +------------ + +* The set of coordination servers can be safely :ref:`changed ` on-the-fly via the CLI. + +* Unintentional deletion of the coordination state files is now ACID-safe and self-correcting when a majority of the state files still exist. + +* The :ref:`foundationdb.conf ` file format has changed. + +* A new more flexible and automatic system for :ref:`network configuration `. Common server setups will auto-configure using the cluster file. More advanced setups are supported via separate configurable listen and public addresses. + +* The CLI now support tab-completion. + +* The CLI now supports setting transaction options + +* The CLI has a new command "getrangekeys" that returns the keys in a range and omits the values. + +* The database size estimate shown in the CLI status is much more accurate. + +Performance +-------------- + +* Improved latency performance for intense workloads with range-read operations. + +* Improved performance and decreased memory usage for certain intense write workloads targeting a small set of keys (such as sequential insert). + +Fixes +-------- + +* An incorrect result could be returned by a range read when: (1) The range start was specified using a non-default "less than" type key selector; and (2) the range read started at the beginning of the database; and (3) the transaction also included a prior write to a key less than the key of the begin key selector. + +* In certain cases a FoundationDB cluster would not correctly re-configure itself to achieve a more optimal usage of servers of specific machine classes. + +Changes to all APIs +------------------- + +* The API version has been updated from 14 to 16. (Thanks to our API versioning technology, programs requesting API version 14 will work unmodified.) + +* Calling the :py:meth:`reset ` method of a transaction now also resets transaction options. + +* :ref:`System keys ` (those beginning with the byte ``0xFF``) are now inaccessible by default. + +* Simpler network setup: The network connection options are no longer necessary and have been deprecated. + +* Three new transaction options (:py:meth:`READ_AHEAD_DISABLE `, :py:meth:`READ_YOUR_WRITES_DISABLE `, and :py:meth:`ACCESS_SYSTEM_KEYS `) enable more control for advanced applications. + +Changes to the Java API +------------------------ + +* A new construct `AsyncUtil.whileTrue() <../javadoc/com/apple/cie/foundationdb/async/AsyncUtil.html#whileTrue-com.apple.foundationdb.async.Function->`_ simplifies writing loops using the asynchronous version of the Java FDB client. + +Earlier release notes +--------------------- + +For changes in alpha 5, see :doc:`Release Notes (Alpha 5) `. diff --git a/documentation/sphinx/source/old-release-notes/release-notes-021.rst b/documentation/sphinx/source/old-release-notes/release-notes-021.rst index 38fb67d0ade..425452cdf74 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-021.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-021.rst @@ -1,85 +1,85 @@ -###################### -Release Notes (Beta 1) -###################### - -Beta 1 -====== - -Platform support ----------------- - -* Added AWS CloudFormation support for FoundationDB. - -Features --------- - -* Servers can be safely :ref:`removed ` from the cluster. - -* :ref:`Improved status ` with information about database configuration, health, workload, and performance. - -* Improved resiliency against low disk space conditions. - -* The CLI can automatically choose :ref:`coordination servers `. - -* The CLI allows multiple semicolon separated commands per line; a new --exec flag was added to the CLI to pass commands to the CLI and quit when done. - -* Old :ref:`log files ` are automatically deleted. - -* More specific :ref:`error codes `. - -Performance ------------ - -* Reduced latency of getRange when iterating through large amounts of data. - -* Reduced idle CPU usage. - -* Java API: Join in ArrayUtil is efficient for all container types. - -* Java API: Optimized tuple creation. - -Changes to all APIs -------------------- - -* The API version has been updated from 16 to 21. (Thanks to our API versioning technology, programs requesting earlier API versions will work unmodified.) There are no changes required to migrate from version 16 to 21. - -Fixes ------ - -* Commit could return the error commit_conflict (renamed to not_committed) after the transaction successfully committed. (This was previously documented as a known limitation.) - -* If a call to commit returned an error, but onError was not called, the transaction would not be reset. - -* The memory storage engine was too aggressive in reserving disk space. - -* If a key selector in a getRange resolved to the beginning or the end of the database, then its transaction may not have correctly conflicted with other transactions. - -* Ranges passed to clearRange and getRange with the begin key larger than the end could incorrectly cause client API errors. - -* Databases with small amounts of data in them (~20000 bytes) would sometimes slowly move data back and forth between the servers. - -* Large network latencies (> ~250 ms) could impede data balancing between servers. - -* Setting callbacks or calling ``blockUntilReady`` on a future from multiple threads resulted in an error. - -* If a machine running the memory storage engine was killed multiple times in close succession, data loss might occur. - -* C: The headers were not standards compliant and would not compile in some environments. - -* Ruby: API versions were not checked for validity. - -* Windows: The server could crash on non-English versions of Windows. - -* Windows: Manually running fdbserver.exe could fail because of overly restrictive permissions set on shared resources. - -* OS X: Java client had an extraneous linker dependency. - -* Java: In multithreaded conditions, getRange and AsyncUtil.whileTrue() could sometimes never return. - -* Python/Ruby: In multithreaded conditions, the client worker thread could crash. - -Earlier release notes ---------------------- - -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +###################### +Release Notes (Beta 1) +###################### + +Beta 1 +====== + +Platform support +---------------- + +* Added AWS CloudFormation support for FoundationDB. + +Features +-------- + +* Servers can be safely :ref:`removed ` from the cluster. + +* :ref:`Improved status ` with information about database configuration, health, workload, and performance. + +* Improved resiliency against low disk space conditions. + +* The CLI can automatically choose :ref:`coordination servers `. + +* The CLI allows multiple semicolon separated commands per line; a new --exec flag was added to the CLI to pass commands to the CLI and quit when done. + +* Old :ref:`log files ` are automatically deleted. + +* More specific :ref:`error codes `. + +Performance +----------- + +* Reduced latency of getRange when iterating through large amounts of data. + +* Reduced idle CPU usage. + +* Java API: Join in ArrayUtil is efficient for all container types. + +* Java API: Optimized tuple creation. + +Changes to all APIs +------------------- + +* The API version has been updated from 16 to 21. (Thanks to our API versioning technology, programs requesting earlier API versions will work unmodified.) There are no changes required to migrate from version 16 to 21. + +Fixes +----- + +* Commit could return the error commit_conflict (renamed to not_committed) after the transaction successfully committed. (This was previously documented as a known limitation.) + +* If a call to commit returned an error, but onError was not called, the transaction would not be reset. + +* The memory storage engine was too aggressive in reserving disk space. + +* If a key selector in a getRange resolved to the beginning or the end of the database, then its transaction may not have correctly conflicted with other transactions. + +* Ranges passed to clearRange and getRange with the begin key larger than the end could incorrectly cause client API errors. + +* Databases with small amounts of data in them (~20000 bytes) would sometimes slowly move data back and forth between the servers. + +* Large network latencies (> ~250 ms) could impede data balancing between servers. + +* Setting callbacks or calling ``blockUntilReady`` on a future from multiple threads resulted in an error. + +* If a machine running the memory storage engine was killed multiple times in close succession, data loss might occur. + +* C: The headers were not standards compliant and would not compile in some environments. + +* Ruby: API versions were not checked for validity. + +* Windows: The server could crash on non-English versions of Windows. + +* Windows: Manually running fdbserver.exe could fail because of overly restrictive permissions set on shared resources. + +* OS X: Java client had an extraneous linker dependency. + +* Java: In multithreaded conditions, getRange and AsyncUtil.whileTrue() could sometimes never return. + +* Python/Ruby: In multithreaded conditions, the client worker thread could crash. + +Earlier release notes +--------------------- + +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-022.rst b/documentation/sphinx/source/old-release-notes/release-notes-022.rst index 3d7fba85ec9..9b9fa85a478 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-022.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-022.rst @@ -1,65 +1,65 @@ -###################### -Release Notes (Beta 2) -###################### - -Beta 2 -====== - -Features --------- - -* ``fdbcli`` history is stored between sessions; consecutive duplicate commands are stored as a single history entry -* The ``fdbcli`` tool prints a minimal cluster status message if an operation does not complete in 5 seconds. - -Performance ------------ - -* Support for databases up to 100TB (aggregate key-value size). We recommend you contact us for configuration suggestions for databases exceeding 10TB. -* Reduced client CPU usage when returning locally cached values. -* Clients do not write to the database if a value is set to its known current value. -* Improved transaction queuing behavior when a significant portion of transactions are "System Immediate" priority. -* Reduced downtime in certain server-rejoin situations. - -Language APIs -------------- - -* All - - * The API version has been updated from 21 to 22. (Thanks to our API versioning technology, programs requesting earlier API versions will work unmodified.) There are no changes required to migrate from version 21 to 22. - * The ``open()`` call blocks until the client can communicate with the cluster. - -* Node.js - - * Support for Node.js v0.10.x. - * Functions throw errors of type ``FDBError``. - * Removed some variables from the global scope. - -* Java - - * Compiles class files with 1.6 source and target flags. - * Single-jar packaging for all platforms. (In rare cases, setting the ``FDB_LIBRARY_PATH_FDB_JAVA`` environment variable will be requried if you previously relied on loading the library from a system path.) - -* Ruby - - * Support for Ruby on Windows. Requires Ruby version at least 2.0.0 (x64). - * Added implementation of ``on_ready()``. - -Fixes ------ - -* Coordinators could fail to respond if they were busy with other work. -* Fixed a rare segmentation fault on cluster shutdown. -* Fixed an issue where CLI status could sometimes fail. -* Status showed the wrong explanation when performance was limited by system write-to-read latency limit. -* Fixed a rare issue where a "stuck" process trying to participate in the database could run out of RAM. -* Increased robustness of FoundationDB server when loaded with large data sets. -* Eliminated certain cases where the data distribution algorithim could do unnecessary splitting and merging work. -* Several fixes for rare issues encountered by our fault simulation framework. -* Certain uncommon usage of on_ready() in Python could cause segmentation faults. - -Earlier release notes ---------------------- - -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +###################### +Release Notes (Beta 2) +###################### + +Beta 2 +====== + +Features +-------- + +* ``fdbcli`` history is stored between sessions; consecutive duplicate commands are stored as a single history entry +* The ``fdbcli`` tool prints a minimal cluster status message if an operation does not complete in 5 seconds. + +Performance +----------- + +* Support for databases up to 100TB (aggregate key-value size). We recommend you contact us for configuration suggestions for databases exceeding 10TB. +* Reduced client CPU usage when returning locally cached values. +* Clients do not write to the database if a value is set to its known current value. +* Improved transaction queuing behavior when a significant portion of transactions are "System Immediate" priority. +* Reduced downtime in certain server-rejoin situations. + +Language APIs +------------- + +* All + + * The API version has been updated from 21 to 22. (Thanks to our API versioning technology, programs requesting earlier API versions will work unmodified.) There are no changes required to migrate from version 21 to 22. + * The ``open()`` call blocks until the client can communicate with the cluster. + +* Node.js + + * Support for Node.js v0.10.x. + * Functions throw errors of type ``FDBError``. + * Removed some variables from the global scope. + +* Java + + * Compiles class files with 1.6 source and target flags. + * Single-jar packaging for all platforms. (In rare cases, setting the ``FDB_LIBRARY_PATH_FDB_JAVA`` environment variable will be requried if you previously relied on loading the library from a system path.) + +* Ruby + + * Support for Ruby on Windows. Requires Ruby version at least 2.0.0 (x64). + * Added implementation of ``on_ready()``. + +Fixes +----- + +* Coordinators could fail to respond if they were busy with other work. +* Fixed a rare segmentation fault on cluster shutdown. +* Fixed an issue where CLI status could sometimes fail. +* Status showed the wrong explanation when performance was limited by system write-to-read latency limit. +* Fixed a rare issue where a "stuck" process trying to participate in the database could run out of RAM. +* Increased robustness of FoundationDB server when loaded with large data sets. +* Eliminated certain cases where the data distribution algorithim could do unnecessary splitting and merging work. +* Several fixes for rare issues encountered by our fault simulation framework. +* Certain uncommon usage of on_ready() in Python could cause segmentation faults. + +Earlier release notes +--------------------- + +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-023.rst b/documentation/sphinx/source/old-release-notes/release-notes-023.rst index 16742ce16db..17008c7635b 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-023.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-023.rst @@ -1,64 +1,64 @@ -###################### -Release Notes (Beta 3) -###################### - -Beta 3 -====== - -The Beta 3 release focuses on major improvements across our language APIs, including new capabilities for locality, watches, transaction cancellation and timeouts, explicit conflict ranges, and atomic operations. It also improves performance and removes known limitations. - -Features --------- - -* Discover where keys are physically stored using the new :ref:`locality ` API. -* Create :ref:`watches ` that asynchronously report changes to the values of specified keys. -* :ref:`Cancel ` transactions or set them to automatically :ref:`timeout ` and cancel. -* Explicitly add read or write :ref:`conflict ranges `. -* Perform :ref:`atomic operations ` that transform a value (e.g. incrementing it) without client reads to avoid transaction conflicts. -* API version updated to 23. - -Java ----- - -Based on customer feedback and internal testing, the API has been significantly revised for increased performance and ease of use. This is a **breaking** API change. We will continue to make the previous JAR -available for the time being. - -* The asynchronous programming library has been moved to its own package (``com.foundationdb.async``). The library has a host of new members for greater flexibility and more predictable error handling. -* ``Database.run(...)`` can now return an arbitrary object from user code, simplifying use of this recommended retry loop. -* The new interface ``Function`` replaces several interfaces: ``Mapper``, ``Block``, ``Retryable``, and ``AsyncRetryable``. -* Added the ability to cancel any ``Future`` instance, even one not backed with native resources. -* Removed ``onSuccess()`` and ``onFailure()`` in favor of ``map()`` and ``flatMap()``. If code needs simple triggering, ``onReady()`` is still available. -* Range iteration via ``Transaction.getRange(...)`` starts fetching data immediately upon invocation. This simplifies development of code that reads ranges in parallel. -* Many other changes that facilitate writing fast, efficient, and correct Java applications! - -Python ------- - -* Python API methods that :ref:`accept a key ` will also accept a Python object with an ``as_foundationdb_key()`` method that returns a key. Likewise, methods that accept a value will also accept a Python object with an ``as_foundationdb_value()`` method that returns a value. - -Performance ------------ - -* Clients can preferentially communicate with servers on the same machine or in the same datacenter for :ref:`location-aware load balancing `. -* Removed from the client library debugging code included in versions up through Beta 2, leading to higher, more predictable performance. -* Improved data distribution algorithms to optimize data movement during failure scenarios. -* Improved range-read iterators in Node.js using lazy evaluation. -* Improved client-side range-read prefetching in Node.js, Ruby, and Python. -* Incrementally improved performance across all language bindings. - -Fixes ------ - -* A storage node could be prevented from rejoining the cluster until the process was restarted. -* A reverse ``GetRange`` request using a row limit and an end key selector that enters the system keyspace could return too few results. -* A machine power loss immediately following a process restart could result in an invalid transaction log. -* ``GetRange`` could improperly cache too large a range of data when the end key selector resolved past the end of user keyspace, temporarily resulting in incorrect answers to read requests. -* In Node.js, reusing a range iterator for a second request could result in an incomplete result set. - -Earlier release notes ---------------------- - -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +###################### +Release Notes (Beta 3) +###################### + +Beta 3 +====== + +The Beta 3 release focuses on major improvements across our language APIs, including new capabilities for locality, watches, transaction cancellation and timeouts, explicit conflict ranges, and atomic operations. It also improves performance and removes known limitations. + +Features +-------- + +* Discover where keys are physically stored using the new :ref:`locality ` API. +* Create :ref:`watches ` that asynchronously report changes to the values of specified keys. +* :ref:`Cancel ` transactions or set them to automatically :ref:`timeout ` and cancel. +* Explicitly add read or write :ref:`conflict ranges `. +* Perform :ref:`atomic operations ` that transform a value (e.g. incrementing it) without client reads to avoid transaction conflicts. +* API version updated to 23. + +Java +---- + +Based on customer feedback and internal testing, the API has been significantly revised for increased performance and ease of use. This is a **breaking** API change. We will continue to make the previous JAR +available for the time being. + +* The asynchronous programming library has been moved to its own package (``com.foundationdb.async``). The library has a host of new members for greater flexibility and more predictable error handling. +* ``Database.run(...)`` can now return an arbitrary object from user code, simplifying use of this recommended retry loop. +* The new interface ``Function`` replaces several interfaces: ``Mapper``, ``Block``, ``Retryable``, and ``AsyncRetryable``. +* Added the ability to cancel any ``Future`` instance, even one not backed with native resources. +* Removed ``onSuccess()`` and ``onFailure()`` in favor of ``map()`` and ``flatMap()``. If code needs simple triggering, ``onReady()`` is still available. +* Range iteration via ``Transaction.getRange(...)`` starts fetching data immediately upon invocation. This simplifies development of code that reads ranges in parallel. +* Many other changes that facilitate writing fast, efficient, and correct Java applications! + +Python +------ + +* Python API methods that :ref:`accept a key ` will also accept a Python object with an ``as_foundationdb_key()`` method that returns a key. Likewise, methods that accept a value will also accept a Python object with an ``as_foundationdb_value()`` method that returns a value. + +Performance +----------- + +* Clients can preferentially communicate with servers on the same machine or in the same datacenter for :ref:`location-aware load balancing `. +* Removed from the client library debugging code included in versions up through Beta 2, leading to higher, more predictable performance. +* Improved data distribution algorithms to optimize data movement during failure scenarios. +* Improved range-read iterators in Node.js using lazy evaluation. +* Improved client-side range-read prefetching in Node.js, Ruby, and Python. +* Incrementally improved performance across all language bindings. + +Fixes +----- + +* A storage node could be prevented from rejoining the cluster until the process was restarted. +* A reverse ``GetRange`` request using a row limit and an end key selector that enters the system keyspace could return too few results. +* A machine power loss immediately following a process restart could result in an invalid transaction log. +* ``GetRange`` could improperly cache too large a range of data when the end key selector resolved past the end of user keyspace, temporarily resulting in incorrect answers to read requests. +* In Node.js, reusing a range iterator for a second request could result in an incomplete result set. + +Earlier release notes +--------------------- + +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-100.rst b/documentation/sphinx/source/old-release-notes/release-notes-100.rst index 03d9e4bf4fe..2146c6b67e0 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-100.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-100.rst @@ -1,55 +1,55 @@ -################### -Release Notes (1.0) -################### - -1.0.1 -===== - - * Fix segmentation fault in client when there are a very large number of dependent operations in a transaction and certain errors occur. - -1.0.0 -===== - -After a year and a half of Alpha and Beta testing, FoundationDB is now commercially available. Thanks to the help of the thousands of Alpha and Beta testers in our community, we believe that this release is highly robust and capable. - -You can now find pricing and order enterprise licenses online. - -The new Community License now permits free-of-charge use for production systems with up to 6 server processes and for non-production systems with an unlimited number of processes. - -There are only minor technical differences between this release and the 0.3.0 release of August 7, 2013: - -Java ----- - * ``clear(Range)`` replaces the now deprecated ``clearRangeStartsWith()``. - -Python ------- - * Windows installer supports Python 3. - -Node and Ruby -------------- - * String option parameters are converted to UTF-8. - -All ---- - * API version changed to 100. Programs with lower versions continue to work. - * Runs on Mac OS X 10.7. - * Improvements to installation packages, including package paths and directory modes. - * Eliminated cases of excessive resource usage in the locality API. - * Watches are disabled when read-your-writes functionality is disabled. - * Fatal error paths now call ``_exit()`` instead instead of ``exit()``. - -Fixes ------ - * A few Python API entry points failed to respect the ``as_foundationdb_key()`` convenience interface. - * ``fdbcli`` could print commit version numbers incorrectly in Windows. - * Multiple watches set on the same key were not correctly triggered by a subsequent write in the same transaction. - -Earlier release notes ---------------------- - -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (1.0) +################### + +1.0.1 +===== + + * Fix segmentation fault in client when there are a very large number of dependent operations in a transaction and certain errors occur. + +1.0.0 +===== + +After a year and a half of Alpha and Beta testing, FoundationDB is now commercially available. Thanks to the help of the thousands of Alpha and Beta testers in our community, we believe that this release is highly robust and capable. + +You can now find pricing and order enterprise licenses online. + +The new Community License now permits free-of-charge use for production systems with up to 6 server processes and for non-production systems with an unlimited number of processes. + +There are only minor technical differences between this release and the 0.3.0 release of August 7, 2013: + +Java +---- + * ``clear(Range)`` replaces the now deprecated ``clearRangeStartsWith()``. + +Python +------ + * Windows installer supports Python 3. + +Node and Ruby +------------- + * String option parameters are converted to UTF-8. + +All +--- + * API version changed to 100. Programs with lower versions continue to work. + * Runs on Mac OS X 10.7. + * Improvements to installation packages, including package paths and directory modes. + * Eliminated cases of excessive resource usage in the locality API. + * Watches are disabled when read-your-writes functionality is disabled. + * Fatal error paths now call ``_exit()`` instead instead of ``exit()``. + +Fixes +----- + * A few Python API entry points failed to respect the ``as_foundationdb_key()`` convenience interface. + * ``fdbcli`` could print commit version numbers incorrectly in Windows. + * Multiple watches set on the same key were not correctly triggered by a subsequent write in the same transaction. + +Earlier release notes +--------------------- + +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-300.rst b/documentation/sphinx/source/old-release-notes/release-notes-300.rst index c3296d29c69..2fd0fd393cf 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-300.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-300.rst @@ -1,194 +1,194 @@ -################### -Release Notes (3.0) -################### - -3.0.8 -===== - -Release 3.0.8 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: - -* Node.js - updated to 3.0.6 -* Ruby - updated to 3.0.7 -* Java - updated to 3.0.8 - -Fixes ------ -* Backup: the backup agent could crash in some circumstances, preventing a backup from completing. -* Linux: On some systems, disk space usage tracking could be inaccurate. -* In rare cases, range reading could get stuck in an infinite past_version loop. -* Range reading with a begin key selector that resolved to the end of the database might not set the correct conflict range. - -Java ----- -* Fix: getBoundaryKeys could throw a NullPointerException. - -3.0.7 -===== - -Release 3.0.7 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: - -* Node.js - updated to 3.0.6 -* Ruby - updated to 3.0.7 - -Fixes ------ -* ``fdbcli`` would segmentation fault if there was a semicolon after a quoted string. -* :ref:`Atomic operations ` performed on keys that had been :ref:`snapshot read ` would be converted into a set operation. -* Reading a key to which an atomic operation had already been applied would cause the read to behave as a snapshot read. -* In rare scenarios, it was possible for the memory holding the result of a read to be released when a transaction was reset. -* If available RAM was negative, it was reported as a very large number in status. - -Ruby ----- -* Fix: ``FDB`` objects could not be garbage collected. - -3.0.6 -===== - -Release 3.0.6 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: - -* Node.js - updated to 3.0.6 - -Fixes ------ -* Read-latency probes for status incorrectly returned zero. -* Commit-latency probe for status included the time to acquire its read version. -* Client and server could crash when experiencing problems with network connections. - -Node.js -------- -* Fix: npm source package did not compile on Mac OS X 10.9 or newer. - -Windows -------- -* Added registry key during installation. - -3.0.5 -===== - -Release 3.0.5 is protocol-compatible with all prior 3.0.x releases. This release contains only a bug fix for Windows packages; Linux and Mac OS X packages for 3.0.5 are identical to those for 3.0.4. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: - -* Node.js - updated to 3.0.3 if downloaded from ``npm``. - -Fixes ------ -* Windows: fix Visual Studio 2013 code generation bug on older processors or versions of Windows that don't support the AVX instruction set (see https://connect.microsoft.com/VisualStudio/feedback/details/811093). - -3.0.4 -===== - -Release 3.0.4 is protocol-compatible with all prior 3.0.x releases. Users should continue to employ the bindings released with 3.0.2, with the exception of the following: - -* Node.js - updated to 3.0.3 if downloaded from ``npm``. - -Fixes ------ -* Mac OS X: backup agent used 100% CPU even when idle. -* Backups were inoperative on databases with greater than 32-bit versions. -* Backup agents were not started on Windows. -* Restore required write permissions on files. -* The backup client did not report errors properly in all scenarios. -* ``fdbserver -v`` did not print the version. - -Node.js -------- -* Fixed a compilation problem on Linux and Mac OS X as distributed on ``npm``. (Note: The corrected binding is distributed as version 3.0.3.) - -3.0.2 -===== - -Upgrades --------- - -* When upgrading from version 2.0.x to 3.0.x, you should consult :ref:`Upgrading to 3.0 `. - -Features --------- - -* Status information provided in :doc:`machine-readable JSON ` form. -* Differential backups and backup of selective keyspaces added to :ref:`backup tool `. -* Clients may retrieve :ref:`machine-readable status `, :ref:`cluster filepath, and cluster file contents ` by reading designated system keys from the database. -* Two new :ref:`atomic operations `: max and min. - -Performance ------------ - -* Increased maximum writes per second from 200,000 to 11,000,000. -* Improved latencies, particularly on underutilized clusters. -* Improved performance of backup and restore. -* Improved client CPU usage. -* Better rate-limiting when committing very large transactions. -* Improved performance while servers rejoin the cluster. - -Fixes ------ - -* B-tree vacuuming could exhibit poor performance after large deletions of data. -* Computation of memory availability was not correct on newer Linux versions. -* Integers could overflow when setting range limits. -* With the memory storage engine, a key could be lost after multiple reboots in quick succession. - -Client ------- - -* Support for API version 300 and backwards compatible with previous API versions. -* By default, :ref:`snapshot reads ` see writes within the same transaction. The previous behavior can be achieved using transaction options. -* The :ref:`transaction size limit ` includes conflict ranges. -* Explicitly added read or write :ref:`conflict ranges ` and :ref:`watches ` for keys that begin with ``\xFF`` require one of the transaction options ``access_system_keys`` or ``read_system_keys`` to be set. -* New network options for ``trace_max_logs_size`` and ``trace_roll_size`` for an individual client's trace files. -* New transaction options: max_retry_delay, read_system_keys. -* All errors cause :ref:`watches ` to trigger. -* All errors cause a transaction to reset (previously true only of some errors). - -Java ----- - -* ``ReadTransactionContext`` added next to ``TransactionContext``, allowing ``read()`` and ``readAsync()`` composable read-only operations on transactions. -* The ``Future`` interface adds ``getInterruptibly()`` and ``blockInterruptibly()``, which propagate ``InterruptedExcetption`` to the calling code. -* Exception-handling logic is reworked in ``map()``, ``flatMap()``, and ``rescue()`` to propagate ``OutOfMemoryError`` and ``RejectedExecutionException`` instead of the spurious ``SettableAlreadySet`` exception. -* Performance is improved for applications that use many blocking-style ``get()`` calls. - -Node.js -------- -* Fix: ``fdb.open``, ``fdb.createCluster``, and ``cluster.openDatabase`` didn't use the callback in API versions 22 or lower. -* Tuple performance is improved. - -PHP ---- - -* Snapshot reads have a ``transact`` function. - -Python ------- - -* Bindings work in Cygwin. -* The :ref:`transactional decorator ` no longer warns of a transaction approaching the 5 second limit. - -Ruby ----- - -* Fix: ``db.get``, ``get_key``, and ``get_and_watch`` returned Futures instead of actual values. - -Other changes -------------- - -* Versions increase by 1 million per second instead of 1 thousand per second. -* Removed support for Ubuntu 11.10. -* Python binding has been removed from Linux packages. -* In ``fdbcli``, ``getrange`` does a prefix range read if no end key is specified. -* In ``fdbcli``, added an option to disable the initial status check. - -Note on version numbers ------------------------ - -Version 3.0.2 is the first publicly released version in the 3.0.x series. Versions 3.0.0-1 were limited-availability releases with the same feature set. - -Earlier release notes ---------------------- -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (3.0) +################### + +3.0.8 +===== + +Release 3.0.8 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: + +* Node.js - updated to 3.0.6 +* Ruby - updated to 3.0.7 +* Java - updated to 3.0.8 + +Fixes +----- +* Backup: the backup agent could crash in some circumstances, preventing a backup from completing. +* Linux: On some systems, disk space usage tracking could be inaccurate. +* In rare cases, range reading could get stuck in an infinite past_version loop. +* Range reading with a begin key selector that resolved to the end of the database might not set the correct conflict range. + +Java +---- +* Fix: getBoundaryKeys could throw a NullPointerException. + +3.0.7 +===== + +Release 3.0.7 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: + +* Node.js - updated to 3.0.6 +* Ruby - updated to 3.0.7 + +Fixes +----- +* ``fdbcli`` would segmentation fault if there was a semicolon after a quoted string. +* :ref:`Atomic operations ` performed on keys that had been :ref:`snapshot read ` would be converted into a set operation. +* Reading a key to which an atomic operation had already been applied would cause the read to behave as a snapshot read. +* In rare scenarios, it was possible for the memory holding the result of a read to be released when a transaction was reset. +* If available RAM was negative, it was reported as a very large number in status. + +Ruby +---- +* Fix: ``FDB`` objects could not be garbage collected. + +3.0.6 +===== + +Release 3.0.6 is protocol-compatible with all prior 3.0.x releases. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: + +* Node.js - updated to 3.0.6 + +Fixes +----- +* Read-latency probes for status incorrectly returned zero. +* Commit-latency probe for status included the time to acquire its read version. +* Client and server could crash when experiencing problems with network connections. + +Node.js +------- +* Fix: npm source package did not compile on Mac OS X 10.9 or newer. + +Windows +------- +* Added registry key during installation. + +3.0.5 +===== + +Release 3.0.5 is protocol-compatible with all prior 3.0.x releases. This release contains only a bug fix for Windows packages; Linux and Mac OS X packages for 3.0.5 are identical to those for 3.0.4. All users should continue to employ the bindings released with 3.0.2, with the exception of the following: + +* Node.js - updated to 3.0.3 if downloaded from ``npm``. + +Fixes +----- +* Windows: fix Visual Studio 2013 code generation bug on older processors or versions of Windows that don't support the AVX instruction set (see https://connect.microsoft.com/VisualStudio/feedback/details/811093). + +3.0.4 +===== + +Release 3.0.4 is protocol-compatible with all prior 3.0.x releases. Users should continue to employ the bindings released with 3.0.2, with the exception of the following: + +* Node.js - updated to 3.0.3 if downloaded from ``npm``. + +Fixes +----- +* Mac OS X: backup agent used 100% CPU even when idle. +* Backups were inoperative on databases with greater than 32-bit versions. +* Backup agents were not started on Windows. +* Restore required write permissions on files. +* The backup client did not report errors properly in all scenarios. +* ``fdbserver -v`` did not print the version. + +Node.js +------- +* Fixed a compilation problem on Linux and Mac OS X as distributed on ``npm``. (Note: The corrected binding is distributed as version 3.0.3.) + +3.0.2 +===== + +Upgrades +-------- + +* When upgrading from version 2.0.x to 3.0.x, you should consult :ref:`Upgrading to 3.0 `. + +Features +-------- + +* Status information provided in :doc:`machine-readable JSON ` form. +* Differential backups and backup of selective keyspaces added to :ref:`backup tool `. +* Clients may retrieve :ref:`machine-readable status `, :ref:`cluster filepath, and cluster file contents ` by reading designated system keys from the database. +* Two new :ref:`atomic operations `: max and min. + +Performance +----------- + +* Increased maximum writes per second from 200,000 to 11,000,000. +* Improved latencies, particularly on underutilized clusters. +* Improved performance of backup and restore. +* Improved client CPU usage. +* Better rate-limiting when committing very large transactions. +* Improved performance while servers rejoin the cluster. + +Fixes +----- + +* B-tree vacuuming could exhibit poor performance after large deletions of data. +* Computation of memory availability was not correct on newer Linux versions. +* Integers could overflow when setting range limits. +* With the memory storage engine, a key could be lost after multiple reboots in quick succession. + +Client +------ + +* Support for API version 300 and backwards compatible with previous API versions. +* By default, :ref:`snapshot reads ` see writes within the same transaction. The previous behavior can be achieved using transaction options. +* The :ref:`transaction size limit ` includes conflict ranges. +* Explicitly added read or write :ref:`conflict ranges ` and :ref:`watches ` for keys that begin with ``\xFF`` require one of the transaction options ``access_system_keys`` or ``read_system_keys`` to be set. +* New network options for ``trace_max_logs_size`` and ``trace_roll_size`` for an individual client's trace files. +* New transaction options: max_retry_delay, read_system_keys. +* All errors cause :ref:`watches ` to trigger. +* All errors cause a transaction to reset (previously true only of some errors). + +Java +---- + +* ``ReadTransactionContext`` added next to ``TransactionContext``, allowing ``read()`` and ``readAsync()`` composable read-only operations on transactions. +* The ``Future`` interface adds ``getInterruptibly()`` and ``blockInterruptibly()``, which propagate ``InterruptedExcetption`` to the calling code. +* Exception-handling logic is reworked in ``map()``, ``flatMap()``, and ``rescue()`` to propagate ``OutOfMemoryError`` and ``RejectedExecutionException`` instead of the spurious ``SettableAlreadySet`` exception. +* Performance is improved for applications that use many blocking-style ``get()`` calls. + +Node.js +------- +* Fix: ``fdb.open``, ``fdb.createCluster``, and ``cluster.openDatabase`` didn't use the callback in API versions 22 or lower. +* Tuple performance is improved. + +PHP +--- + +* Snapshot reads have a ``transact`` function. + +Python +------ + +* Bindings work in Cygwin. +* The :ref:`transactional decorator ` no longer warns of a transaction approaching the 5 second limit. + +Ruby +---- + +* Fix: ``db.get``, ``get_key``, and ``get_and_watch`` returned Futures instead of actual values. + +Other changes +------------- + +* Versions increase by 1 million per second instead of 1 thousand per second. +* Removed support for Ubuntu 11.10. +* Python binding has been removed from Linux packages. +* In ``fdbcli``, ``getrange`` does a prefix range read if no end key is specified. +* In ``fdbcli``, added an option to disable the initial status check. + +Note on version numbers +----------------------- + +Version 3.0.2 is the first publicly released version in the 3.0.x series. Versions 3.0.0-1 were limited-availability releases with the same feature set. + +Earlier release notes +--------------------- +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-400.rst b/documentation/sphinx/source/old-release-notes/release-notes-400.rst index b076864bea0..cff6ef38c94 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-400.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-400.rst @@ -1,69 +1,69 @@ -################### -Release Notes (4.0) -################### - -4.0.2 -===== - -Fixes ------ - -* Streaming mode ``EXACT`` was ignoring the ``target_bytes`` parameter. - -Java ----- - -* Added a ``toString`` method to the Tuple class. - -4.0.1 -===== - -Fdbcli ------- - -* Added a "configure auto" command which will recommend a setting for proxies and logs (not resolvers) along with machine class changes. -* Added a "setclass" command which can change a processes machine class from the cli. - -Performance ------------ - -* Improved the recovery speed of the transaction subsystem. -* Improved the stability of the transaction rate under saturating workloads. -* Made the transaction log more memory efficient. - -Features --------- - -* Added support for Versionstamp atomic operations. - -Fixes ------ - -* It was not safe to allocate multiple directories concurrently in the same transaction in the directory layer. - -Java ----- - -* Changed the package for the Java bindings from com.foundationdb to com.apple.cie.foundationdb. - -Python ------- - -* Tuple support for integers up to 255 bytes. - -Other changes -------------- - -* Added detailed metric logging available through Scope. -* An optional configuration parameter has been added that allows you to specify a seed cluster file. - -Earlier release notes ---------------------- -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.0) +################### + +4.0.2 +===== + +Fixes +----- + +* Streaming mode ``EXACT`` was ignoring the ``target_bytes`` parameter. + +Java +---- + +* Added a ``toString`` method to the Tuple class. + +4.0.1 +===== + +Fdbcli +------ + +* Added a ``configure auto`` command which will recommend a setting for proxies and logs (not resolvers) along with machine class changes. +* Added a ``setclass`` command which can change a process's machine class from ``fdbcli``. + +Performance +----------- + +* Improved the recovery speed of the transaction subsystem. +* Improved the stability of the transaction rate under saturating workloads. +* Made the transaction log more memory efficient. + +Features +-------- + +* Added support for Versionstamp atomic operations. + +Fixes +----- + +* It was not safe to allocate multiple directories concurrently in the same transaction in the directory layer. + +Java +---- + +* Changed the package for the Java bindings from com.foundationdb to com.apple.cie.foundationdb. + +Python +------ + +* Tuple support for integers up to 255 bytes. + +Other changes +------------- + +* Added detailed metric logging available through Scope. +* An optional configuration parameter has been added that allows you to specify a seed cluster file. + +Earlier release notes +--------------------- +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-410.rst b/documentation/sphinx/source/old-release-notes/release-notes-410.rst index 274d4d90de5..776ccf90b06 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-410.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-410.rst @@ -1,53 +1,53 @@ -################### -Release Notes (4.1) -################### - -4.1.1 -===== - -Fixes ------ - -* Many short-lived file access metrics were being created. -* A completed backup could be improperly marked as incomplete. -* In rare scenarios the resolvers could fail to make progress. - -4.1.0 -===== - -Performance ------------ - -* Significantly improved cluster performance in a wide variety of machine failure scenarios. - -Features --------- - -* Clients can now load multiple versions of the client library, and will gracefully switch to the appropriate version when the server is upgraded. -* A new operating mode for ``fdbbackup`` writes backup data files into the blob store. -* Transactions no longer automatically reset after a successful commit. -* Added ability to set network options with environment variables. -* Added a new API function for determining the value to which atomic versionstamp operations in a transaction were transformed or would have been transformed. -* Improved logic for integrating manually-assigned machine classes with other constraints on role locations. -* Added a new machine class ``stateless`` which is the top priority location for resolvers, proxies, and masters. -* Added a new machine class ``log`` which is the top priority location for transaction logs. -* Trace events are now event metrics that are exposed in Scope. - -Fixes ------ - -* A log could attempt to recover from a partially recovered set of logs when fast recovery was enabled. -* A rare scenario could cause a crash when a master is recovering metadata from the previous generation of logs. -* Streaming mode ``EXACT`` was ignoring the ``target_bytes`` parameter. - -Earlier release notes ---------------------- -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.1) +################### + +4.1.1 +===== + +Fixes +----- + +* Many short-lived file access metrics were being created. +* A completed backup could be improperly marked as incomplete. +* In rare scenarios the resolvers could fail to make progress. + +4.1.0 +===== + +Performance +----------- + +* Significantly improved cluster performance in a wide variety of machine failure scenarios. + +Features +-------- + +* Clients can now load multiple versions of the client library, and will gracefully switch to the appropriate version when the server is upgraded. +* A new operating mode for ``fdbbackup`` writes backup data files into the blob store. +* Transactions no longer automatically reset after a successful commit. +* Added ability to set network options with environment variables. +* Added a new API function for determining the value to which atomic versionstamp operations in a transaction were transformed or would have been transformed. +* Improved logic for integrating manually-assigned machine classes with other constraints on role locations. +* Added a new machine class ``stateless`` which is the top priority location for resolvers, proxies, and masters. +* Added a new machine class ``log`` which is the top priority location for transaction logs. +* Trace events are now event metrics that are exposed in Scope. + +Fixes +----- + +* A log could attempt to recover from a partially recovered set of logs when fast recovery was enabled. +* A rare scenario could cause a crash when a master is recovering metadata from the previous generation of logs. +* Streaming mode ``EXACT`` was ignoring the ``target_bytes`` parameter. + +Earlier release notes +--------------------- +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-420.rst b/documentation/sphinx/source/old-release-notes/release-notes-420.rst index 081df4884d5..44b526e8127 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-420.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-420.rst @@ -1,35 +1,35 @@ -################### -Release Notes (4.2) -################### - -4.2.1 -===== - -Fixes ------ - -* The Java bindings had an incorrectly named native extension on Linux. - -4.2.0 -===== - -Features --------- - -* A new utility allows :doc:`Backups ` of a cluster directly into another FoundationDB database. It is designed also to support asynchronous replication and disaster recovery. -* A new version of the `Java bindings <../javadoc-completable/index.html>`_ adds support for Java 8 Completable Futures. -* Information on the versions of connected clients has been added to :doc:`Machine-Readable Status `. -* Information on the status of running backups has been added to :doc:`Machine-Readable Status `. - -Earlier release notes ---------------------- -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.2) +################### + +4.2.1 +===== + +Fixes +----- + +* The Java bindings had an incorrectly named native extension on Linux. + +4.2.0 +===== + +Features +-------- + +* A new utility allows :doc:`Backups ` of a cluster directly into another FoundationDB database. It is designed also to support asynchronous replication and disaster recovery. +* A new version of the `Java bindings <../javadoc-completable/index.html>`_ adds support for Java 8 Completable Futures. +* Information on the versions of connected clients has been added to :doc:`Machine-Readable Status `. +* Information on the status of running backups has been added to :doc:`Machine-Readable Status `. + +Earlier release notes +--------------------- +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-430.rst b/documentation/sphinx/source/old-release-notes/release-notes-430.rst index 650d66aba5f..83f04c5b4b5 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-430.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-430.rst @@ -1,37 +1,37 @@ -################### -Release Notes (4.3) -################### - -4.3.0 -===== - -Features --------- - -* Improved DR thoughput by having mutations copied into the DR database before applying them. -* Renamed db_agent to dr_agent. -* Added more detailed DR and backup active task detail into layer status. - -Fixes ------ - -* Backup seconds behind did not update in continuous mode. -* DR layer status did not report correctly. -* The Java bindings had an incorrectly named native extension on Linux. -* DR status would crash if called before a DR had been started. -* Changed the blob restore read pattern to work around blob store issues. -* External clients do not load environment variable options. - -Earlier release notes ---------------------- -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.3) +################### + +4.3.0 +===== + +Features +-------- + +* Improved DR thoughput by having mutations copied into the DR database before applying them. +* Renamed db_agent to dr_agent. +* Added more detailed DR and backup active task detail into layer status. + +Fixes +----- + +* Backup seconds behind did not update in continuous mode. +* DR layer status did not report correctly. +* The Java bindings had an incorrectly named native extension on Linux. +* DR status would crash if called before a DR had been started. +* Changed the blob restore read pattern to work around blob store issues. +* External clients do not load environment variable options. + +Earlier release notes +--------------------- +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-440.rst b/documentation/sphinx/source/old-release-notes/release-notes-440.rst index 1b5926573f9..cc0014964cc 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-440.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-440.rst @@ -1,73 +1,73 @@ -################### -Release Notes (4.4) -################### - -4.4.2 -===== - -Features --------- - -* Backup's minimum unit of progress is now a single committed version, allowing progress to be made when the database is very unhealthy. - -Fixes --------- - -* Options being disabled in fdbcli required an unnecessary parameter. -* In rare situations, an incorrect backup index could be written. Contact us if you need to restore data from a v4.4.1 or earlier backup. -* A crash could occur on startup in fdbbackup and fdbcli. -* A data corruption bug observed on OS X was fixed. The issue has never been observed on other platforms. - -4.4.1 -===== - -Features --------- - -* Added support for streaming writes. This allows a client to load an ordered list of mutations into the database in parallel, and once they are all loaded, the mutations will be applied to the database in order. -* DR uses streaming writes to significantly improve throughput. -* Restore was rewritten so that many clients can partipate in restoring data, significantly improving restore speed. The command line restore tool interface has been updated to support this new capability. -* Cluster files now support comments (using the '#' character). -* A wide variety of new client-side statistics are logged in client trace files every 5 seconds. -* Status reports the generation of the system. The generation is incremented every time there is a failure (and recovery) in the transaction subsystem. -* Added a new machine-wide identification token. This token is used in place of the user-supplied "machine ID" in instances where true physical machine is the unit of interest. This change will allow for reporting tools to output the actual number of physical machines present in a cluster. -* Added per-process metrics for total disk capacity and free space to status json output that allow for more repeatable and expected reporting of host disk usage. These metrics are based on the "data-dir" parameter to fdbserver and will be reported without regard to whether the process is using the disk or not. -* Added backup size estimates to status json output. -* Added process uptime seconds to status json output. -* Added a flag indicating whether the database is locked to status json output. - -Fixes ------ - -* Only processes which can become logs are counted towards fault tolerance. -* A long running process would have a local estimate of time which differed greatly from system clock of the machine the process was running on. -* DR errors were not being reported properly in DR status. -* Backup and DR layer status expiration and cleanup now use database read version instead of time. - -Java ----- - -* The `ReadTransaction` interface supports the ability to set transaction options. - -Other Changes -------------- - -* Removed support for the old log system (pre 3.0). To upgrade to 4.4+ from a version before 3.0, first upgrade to a version between 3.0 and 4.3. -* Removed trace event spam in backup and DR. -* Backup and DR only report the most recent error, rather than a list of errors. -* Updated language binding 'API version not supported' error message to include the version requested and supported. - -Earlier release notes ---------------------- -* :doc:`4.3 (API Version 430) ` -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.4) +################### + +4.4.2 +===== + +Features +-------- + +* Backup's minimum unit of progress is now a single committed version, allowing progress to be made when the database is very unhealthy. + +Fixes +-------- + +* Options being disabled in fdbcli required an unnecessary parameter. +* In rare situations, an incorrect backup index could be written. Contact us if you need to restore data from a v4.4.1 or earlier backup. +* A crash could occur on startup in fdbbackup and fdbcli. +* A data corruption bug observed on OS X was fixed. The issue has never been observed on other platforms. + +4.4.1 +===== + +Features +-------- + +* Added support for streaming writes. This allows a client to load an ordered list of mutations into the database in parallel, and once they are all loaded, the mutations will be applied to the database in order. +* DR uses streaming writes to significantly improve throughput. +* Restore was rewritten so that many clients can partipate in restoring data, significantly improving restore speed. The command line restore tool interface has been updated to support this new capability. +* Cluster files now support comments (using the '#' character). +* A wide variety of new client-side statistics are logged in client trace files every 5 seconds. +* Status reports the generation of the system. The generation is incremented every time there is a failure (and recovery) in the transaction subsystem. +* Added a new machine-wide identification token. This token is used in place of the user-supplied "machine ID" in instances where true physical machine is the unit of interest. This change will allow for reporting tools to output the actual number of physical machines present in a cluster. +* Added per-process metrics for total disk capacity and free space to status json output that allow for more repeatable and expected reporting of host disk usage. These metrics are based on the "data-dir" parameter to fdbserver and will be reported without regard to whether the process is using the disk or not. +* Added backup size estimates to status json output. +* Added process uptime seconds to status json output. +* Added a flag indicating whether the database is locked to status json output. + +Fixes +----- + +* Only processes which can become logs are counted towards fault tolerance. +* A long running process would have a local estimate of time which differed greatly from system clock of the machine the process was running on. +* DR errors were not being reported properly in DR status. +* Backup and DR layer status expiration and cleanup now use database read version instead of time. + +Java +---- + +* The `ReadTransaction` interface supports the ability to set transaction options. + +Other Changes +------------- + +* Removed support for the old log system (pre 3.0). To upgrade to 4.4+ from a version before 3.0, first upgrade to a version between 3.0 and 4.3. +* Removed trace event spam in backup and DR. +* Backup and DR only report the most recent error, rather than a list of errors. +* Updated language binding 'API version not supported' error message to include the version requested and supported. + +Earlier release notes +--------------------- +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-450.rst b/documentation/sphinx/source/old-release-notes/release-notes-450.rst index 01b7dc4ecde..439777f620b 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-450.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-450.rst @@ -1,143 +1,143 @@ -################### -Release Notes (4.5) -################### - -4.5.6 -===== - -Fixes ------ - -* Disabled debug SevError trace event when applyMutations is cancelled -* Fixed problem skipping publishable files -* Publish debug symbols for files for all platforms - -4.5.5 -===== - -Fixes ------ - -* DR and backup restoration could stall when encountering transactions larger than 5 MB. -* The ``_valid`` field in layer status was missing when the client couldn't communicate with the cluster. -* Backup uploads to blobstore were not verifying their checksums. - -4.5.4 -===== - -Fixes ------ - -* Creating transactions with the multi-version client was not thread safe. -* The status latency probe is now performed separately from other status gathering code. -* Watches could fire early needlessly and in rare cases may not fire when they should. -* Change the type of the ``query_queue_max`` attribute in status from an object to an integer. -* ``fdbserver`` arguments were not properly preserved in log output. -* Increase priority of getting read versions over commits on the master proxy. -* Java: Inserting a non-Long number into a Tuple and reading it back out threw a ClassCastException. - -4.5.3 -===== - -Fdbcli ------- - -* Fix: key and value output did not escape the backslash ('\\') character. -* Spaces in key and value output are no longer escaped. - -Java-completable ----------------- - -* Fix: Range queries with a limit could sometimes return fewer items than requested. - -Status ------- - -* Fix: backup and DR ``range_bytes_written`` and ``mutation_log_bytes_written`` are reset when backup or DR is restarted. -* Added ``_valid`` and ``_error`` fields to layer status. - -4.5.2 -===== - -Fixes ------ - -* Fixed thread safety issues with backup and DR. -* Fixed a load balancing problem when a machine was already failed when a client starts. -* Golang: adding an explicit conflict key included the key immediately following in the conflict range. - -4.5.1 -===== - -Features --------- - -* When a process is added to a cluster, it will be given data at a much faster rate. -* Improved the speed at which the cluster reacts to a failed process. - -Fixes ------ - -* Calculating status in large databases could cause slow tasks. -* Fdbrestore printed an error when passed the ``--help`` flag. -* A multi-version API external client that was a duplicate could cause a crash at client shutdown. -* A multi-version API external client that failed to load would cause a crash at client shutdown. -* Status could report an incorrect database size in rare scenarios. -* Data distribution would stopped working (both exclude and new recruitment) if a call to commit on the storage server could hang indefinitely. -* ``fdbcli`` would quit on some commands that were not errors or continue after some real errors when run with ``--exec``. -* Trace files generated by clients using the multi-version API could have name collisions. - -Fdbcli ------- - -* Client issues are now aggregated by name and have an addresses list. Status details prints a truncated view of these. -* Status json lists incompatible clients. -* Status json includes processes that couldn't be reached in the processes list. -* Exclude does not run if it will drop free space below 10%. This check can be bypassed by adding the word ``FORCE`` as a parameter. -* Added write mode, which must be turned on to set or clear keys. -* Added the ``kill`` command for killing processes. - -Fdbmonitor ----------- - -* The restart logic for dead child processes now uses a backoff. -* Added option to remove WD40 environment variables. -* ``fdbmonitor`` will create the lockfile directory if it doesn't exist. -* Added support for monitoring symbolic link changes in the configuration file path. -* Added an option to disable killing of child processes when the configuration changes. -* Added support for larger process IDs. - -Backup ------- - -* Blobstore URLs can now contain multiple IP addresses, separated by commas, over which to load balance requests. - -Bindings --------- - -* Add error predicate testing to client bindings. This new functionality should help complex use cases write correct transaction retry loops where dispatching on error classes is needed. - -Other Changes -------------- - -* Maximum shard size increased from 100MB to 500MB. -* Support backslash as an escape character for semicolons and backslashes when setting network options using environment variables. -* Add ``logGroup`` attribute to rolled trace events. -* Calling get range with a begin key == ``\xff\xff/worker_interfaces`` will return a list of serialized worker interfaces. Calling set with the key ``\xff\xff/reboot_worker`` and a value which is a serialized worker interface will reboot that process. - - -Earlier release notes ---------------------- -* :doc:`4.4 (API Version 440) ` -* :doc:`4.3 (API Version 430) ` -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +################### +Release Notes (4.5) +################### + +4.5.6 +===== + +Fixes +----- + +* Disabled debug SevError trace event when applyMutations is cancelled +* Fixed problem skipping publishable files +* Publish debug symbols for files for all platforms + +4.5.5 +===== + +Fixes +----- + +* DR and backup restoration could stall when encountering transactions larger than 5 MB. +* The ``_valid`` field in layer status was missing when the client couldn't communicate with the cluster. +* Backup uploads to blobstore were not verifying their checksums. + +4.5.4 +===== + +Fixes +----- + +* Creating transactions with the multi-version client was not thread safe. +* The status latency probe is now performed separately from other status gathering code. +* Watches could fire early needlessly and in rare cases may not fire when they should. +* Change the type of the ``query_queue_max`` attribute in status from an object to an integer. +* ``fdbserver`` arguments were not properly preserved in log output. +* Increase priority of getting read versions over commits on the master proxy. +* Java: Inserting a non-Long number into a Tuple and reading it back out threw a ClassCastException. + +4.5.3 +===== + +Fdbcli +------ + +* Fix: key and value output did not escape the backslash ('\\') character. +* Spaces in key and value output are no longer escaped. + +Java-completable +---------------- + +* Fix: Range queries with a limit could sometimes return fewer items than requested. + +Status +------ + +* Fix: backup and DR ``range_bytes_written`` and ``mutation_log_bytes_written`` are reset when backup or DR is restarted. +* Added ``_valid`` and ``_error`` fields to layer status. + +4.5.2 +===== + +Fixes +----- + +* Fixed thread safety issues with backup and DR. +* Fixed a load balancing problem when a machine was already failed when a client starts. +* Golang: adding an explicit conflict key included the key immediately following in the conflict range. + +4.5.1 +===== + +Features +-------- + +* When a process is added to a cluster, it will be given data at a much faster rate. +* Improved the speed at which the cluster reacts to a failed process. + +Fixes +----- + +* Calculating status in large databases could cause slow tasks. +* Fdbrestore printed an error when passed the ``--help`` flag. +* A multi-version API external client that was a duplicate could cause a crash at client shutdown. +* A multi-version API external client that failed to load would cause a crash at client shutdown. +* Status could report an incorrect database size in rare scenarios. +* Data distribution would stop working (both exclude and new recruitment) if a call to commit on the storage server could hang indefinitely. +* ``fdbcli`` would quit on some commands that were not errors or continue after some real errors when run with ``--exec``. +* Trace files generated by clients using the multi-version API could have name collisions. + +Fdbcli +------ + +* Client issues are now aggregated by name and have an addresses list. Status details prints a truncated view of these. +* Status json lists incompatible clients. +* Status json includes processes that couldn't be reached in the processes list. +* Exclude does not run if it will drop free space below 10%. This check can be bypassed by adding the word ``FORCE`` as a parameter. +* Added write mode, which must be turned on to set or clear keys. +* Added the ``kill`` command for killing processes. + +Fdbmonitor +---------- + +* The restart logic for dead child processes now uses a backoff. +* Added option to remove WD40 environment variables. +* ``fdbmonitor`` will create the lockfile directory if it doesn't exist. +* Added support for monitoring symbolic link changes in the configuration file path. +* Added an option to disable killing of child processes when the configuration changes. +* Added support for larger process IDs. + +Backup +------ + +* Blobstore URLs can now contain multiple IP addresses, separated by commas, over which to load balance requests. + +Bindings +-------- + +* Add error predicate testing to client bindings. This new functionality should help complex use cases write correct transaction retry loops where dispatching on error classes is needed. + +Other Changes +------------- + +* Maximum shard size increased from 100MB to 500MB. +* Support backslash as an escape character for semicolons and backslashes when setting network options using environment variables. +* Add ``logGroup`` attribute to rolled trace events. +* Calling get range with a begin key == ``\xff\xff/worker_interfaces`` will return a list of serialized worker interfaces. Calling set with the key ``\xff\xff/reboot_worker`` and a value which is a serialized worker interface will reboot that process. + + +Earlier release notes +--------------------- +* :doc:`4.4 (API Version 440) ` +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-460.rst b/documentation/sphinx/source/old-release-notes/release-notes-460.rst index ea97ca0fc98..e4ce8e91f6d 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-460.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-460.rst @@ -1,130 +1,130 @@ -############# -Release Notes -############# - -4.6.5 -===== - -Bindings --------- - -* Java bindings now perform marshaling off of the network thread. - - -4.6.4 -===== - -Features --------- - -* Added ability to use --io_trust_seconds in a warn-only mode, which logs a trace event rather than failing the process when a disk operation takes a long time. This is enabled with --io_trust_warn_only. - -Fixes ------ - -* Disk operation timeouts now cause the process to restart rather than hang indefinitely. -* ``fdbdr switch`` did not start the DR in the opposite direction correctly, resulting in mutations being lost. -* Lowered backup and DR batch sizes to avoid large packet warnings. -* Remove partial pipelining of tlog commits. - -4.6.3 -===== - -Features --------- - -* Added the ability to run a consistency check of a database using a new server role. - -Fixes ------ - -* Added the ability to automatically shutdown processes if a disk operation takes a long time to complete. This is enabled with --io_trust_seconds. -* Too many outstanding storage recruitment requests causes the cluster controller to hang. -* Corrected issue with Ubuntu installer package on Ubuntu 16.04 not starting daemon. -* Package non-Linux builds of JNI component into Java jars. -* Published backup-related binaries on macOS were incorrectly pointing to symbolic link specification files. - -Performance ------------ - -* We no longer fsync trace files. -* Lowered the default bandwidth shard splitting knobs for better performance with hot key ranges. - -4.6.2 -===== - -Fixes ------ - -* The tlog could commit more than 100MB at a time -* Metrics with filename component not present in trace events -* Setting new locality information causes missing process metrics in status details -* FDB processes killed via CLI could hang while killing themselves -* Enabled recovery of on-disk data files in the event of a very specific rare corruption situation -* Process messages get reported as errors by status, but don't get attributed to a process in the status details list -* DR prematurely reported progress for work that needed to be retried - -Performance ------------ - -* Storage engine performance improvements to reduce the overhead that ssd-2 requires for its benefits over ssd-1 -* Lowered the default fetch keys parallelism to slow down data distribution - -4.6.1 -===== - -Fixes ------ - -* Starting a new DR on a large database can cause the secondary cluster to lose availability -* Secondary clusters that have been upgraded were reporting "primary" metrics -* Backup and DR could get stuck if too many tasks timed out simultaneously - -4.6.0 -===== - -Features --------- - -* Added a new storage engine type ``ssd-2`` that includes page checksums and more efficient storage of large values. The previous storage engine has been renamed ``ssd-1``, and the storage engine ``ssd`` is an alias for ``ssd-2``. -* DR and Restore won't overwrite a non empty database - -Performance ------------ - -* Improve performance of the ssd storage engine in databases with large keys or values -* Improved cluster recovery speed -* Restore is faster due to better load leveling across the keyspace -* Reduced the conflict ranges applied for get range calls in rare cases - -Fixes ------ - -* Backup to Blobstore sends and verifies MD5 sums for uploads -* Backup restoration could be unrestorable in certain cases -* Clients using the multi-version client functionality would incorrectly report incompatible connections in status -* Backup and DR network metrics were incorrectly reported as 0 -* Java: fix race condition when removing an empty directory which could lead to a NoSuchElementException -* Fixed a source of potential crashes in fdbcli - -Status ------- - -* The following fields were added: cluster.data.moving_data.total_written_bytes, cluster.qos.limiting_queue_bytes_storage_server, cluster.qos.worst_version_lag_storage_server, cluster.qos.limiting_version_lag_storage_server, cluster.qos.transaction_per_second_limit, cluster.qos.released_transactions_per_second, cluster.qos.performance_limited_by.reason_id, and cluster.database_available - -Earlier release notes ---------------------- -* :doc:`4.5 (API Version 450) ` -* :doc:`4.4 (API Version 440) ` -* :doc:`4.3 (API Version 430) ` -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +############# +Release Notes +############# + +4.6.5 +===== + +Bindings +-------- + +* Java bindings now perform marshaling off of the network thread. + + +4.6.4 +===== + +Features +-------- + +* Added ability to use --io_trust_seconds in a warn-only mode, which logs a trace event rather than failing the process when a disk operation takes a long time. This is enabled with --io_trust_warn_only. + +Fixes +----- + +* Disk operation timeouts now cause the process to restart rather than hang indefinitely. +* ``fdbdr switch`` did not start the DR in the opposite direction correctly, resulting in mutations being lost. +* Lowered backup and DR batch sizes to avoid large packet warnings. +* Remove partial pipelining of tlog commits. + +4.6.3 +===== + +Features +-------- + +* Added the ability to run a consistency check of a database using a new server role. + +Fixes +----- + +* Added the ability to automatically shutdown processes if a disk operation takes a long time to complete. This is enabled with --io_trust_seconds. +* Too many outstanding storage recruitment requests causes the cluster controller to hang. +* Corrected issue with Ubuntu installer package on Ubuntu 16.04 not starting daemon. +* Package non-Linux builds of JNI component into Java jars. +* Published backup-related binaries on macOS were incorrectly pointing to symbolic link specification files. + +Performance +----------- + +* We no longer fsync trace files. +* Lowered the default bandwidth shard splitting knobs for better performance with hot key ranges. + +4.6.2 +===== + +Fixes +----- + +* The tlog could commit more than 100MB at a time +* Metrics with filename component not present in trace events +* Setting new locality information causes missing process metrics in status details +* FDB processes killed via CLI could hang while killing themselves +* Enabled recovery of on-disk data files in the event of a very specific rare corruption situation +* Process messages get reported as errors by status, but don't get attributed to a process in the status details list +* DR prematurely reported progress for work that needed to be retried + +Performance +----------- + +* Storage engine performance improvements to reduce the overhead that ssd-2 requires for its benefits over ssd-1 +* Lowered the default fetch keys parallelism to slow down data distribution + +4.6.1 +===== + +Fixes +----- + +* Starting a new DR on a large database can cause the secondary cluster to lose availability +* Secondary clusters that have been upgraded were reporting "primary" metrics +* Backup and DR could get stuck if too many tasks timed out simultaneously + +4.6.0 +===== + +Features +-------- + +* Added a new storage engine type ``ssd-2`` that includes page checksums and more efficient storage of large values. The previous storage engine has been renamed ``ssd-1``, and the storage engine ``ssd`` is an alias for ``ssd-2``. +* DR and Restore won't overwrite a non empty database + +Performance +----------- + +* Improve performance of the ssd storage engine in databases with large keys or values +* Improved cluster recovery speed +* Restore is faster due to better load leveling across the keyspace +* Reduced the conflict ranges applied for get range calls in rare cases + +Fixes +----- + +* Backup to Blobstore sends and verifies MD5 sums for uploads +* Backup restoration could be unrestorable in certain cases +* Clients using the multi-version client functionality would incorrectly report incompatible connections in status +* Backup and DR network metrics were incorrectly reported as 0 +* Java: fix race condition when removing an empty directory which could lead to a NoSuchElementException +* Fixed a source of potential crashes in fdbcli + +Status +------ + +* The following fields were added: cluster.data.moving_data.total_written_bytes, cluster.qos.limiting_queue_bytes_storage_server, cluster.qos.worst_version_lag_storage_server, cluster.qos.limiting_version_lag_storage_server, cluster.qos.transaction_per_second_limit, cluster.qos.released_transactions_per_second, cluster.qos.performance_limited_by.reason_id, and cluster.database_available + +Earlier release notes +--------------------- +* :doc:`4.5 (API Version 450) ` +* :doc:`4.4 (API Version 440) ` +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-500.rst b/documentation/sphinx/source/old-release-notes/release-notes-500.rst index a56b6b7c1c6..8babcbdaa61 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-500.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-500.rst @@ -1,176 +1,190 @@ -############# -Release Notes -############# - -5.0.7 -===== - -Fixes ------ - -* Blob backups became corrupt when handling non-retryable errors. -* Blob backup did not retry all http errors correctly. - -5.0.6 -===== - -5.0.5 -===== - -Fixes ------ - -* Set a default memory limit of 8GB on all backup and DR executables. This limit is configurable on the command line. -* The backup agent would keep attempting to write a file to blob for up to an hour after the task was cancelled. -* Incorrect blob backup destination URLs could be parsed as correct but missing IP addresses. -* Blob load balancing and per address connection limits have been improved. -* Fdbmonitor now supports 0-parameter flags. -* The read latencies reported in status were higher than what clients observed. - -5.0.4 -===== - -Fixes ------ - -* Logs continued to make their data persistent to disk after being removed. -* Removed logs did not delete their data before shutting down. -* In rare scenarios, a disk error which occured during log recruitment could cause the recruitment to hang indefinately. - -5.0.3 -===== - -Fixes ------ - -* In rare scenarios, recovery could get stuck for 10 minutes. -* The consistency check did not work on locked databases. -* In rare scenarios, backup, DR, or fdbcli could hang indefinitely. -* Some transaction log metrics were not being reported. -* Some network metrics were reported incorrectly as extremely large numbers. - -5.0.2 -===== - -Fixes ------ - -* Functionality to slowly delete large files with incremental truncation was not enabled. -* Fixed a source of crashes from fdbcli. -* Data distribution was prematurely reporting that it had started. - -Bindings --------- - -* Go: Use fully-qualified import paths for fdb dependencies. - -Other ------ - -* Publish header files and static libraries for flow and flow bindings on Linux and macOS. - -5.0.1 -===== - -Fixes ------ - -* Bytes input and bytes durable on the log would drift apart due to rounding errors. - -5.0.0 -===== - -Features --------- - -* All recoveries no longer copy log data before completion. As a result, the fast_recovery_double and fast_recovery_triple configurations have been removed. -* Added a new configuration ``three_data_hall`` where a single data hall failure cannot bring down a cluster. -* Multiple log processes can be within the same zone. -* Clients have access to sampled latency statistics for their operations. -* Added network checksums. -* Fault tolerance is restored much quicker after a storage server failure. - -Performance ------------ - -* Improved recovery speed after rebooting the cluster. -* Improved saturation performance of write-heavy workloads. -* We no longer require extra log durability for fast recoveries. -* Backup/DR now use far less cluster resources while idle. -* Improved load balancing performance. -* Reduced conflict range sizes when performing get range queries with key selectors such that the resolved begin key is greater than or equal to the resolved end key. -* Added functionality to slowly delete large files with incremental truncation. - -Fixes ------ - -* Fixed a pathology where multiple successive failures could lead to a long 30+ minute availability outage. -* Updated status to have failures of old tlogs included in the failure tolerance calculation. -* The fdbserver and fdbbackup processes could return a successful error code after a fatal error. -* Fault tolerance did not reflect coordinators sharing the same machine ID. -* Prevent the DR seconds behind measurement from potentially returning a negative amount. -* Increased the priority of all cluster controller work to prevent the cluster controller from being starved by other work on the same process. -* Fixed a rare crash in the DR agent. -* fdbcli and fdb_c clients logs had 0 values for most ProcessMetrics log event fields. -* DR could get stuck if the time required to copy range data was longer than the task timeout. - -Status ------- - -* Improved latency probe accuracy when the cluster is loaded. -* Report GRV latencies at all priorities in the latency probe. -* For the SSD storage engine, available disk space now includes space within data files that is not currently in use and can be reused. -* Storage servers report how far they are lagging behind the logs. ``fdbcli`` now reports servers that are lagging sufficiently far behind. -* Status json "incompatible_connections" did not work with multiversion clients. -* Added connection counts and establish/close metrics to status json.  - -Bindings --------- - -* API version updated to 500. -* Tuples now support single- and double-precision floating point numbers, UUIDs, booleans, and nested tuples. -* Add ``TRANSACTION_LOGGING_ENABLE`` transaction option that causes the details of a transaction's operations to be logged to the client trace logs. -* Add ``USED_DURING_COMMIT_PROTECTION_DISABLE`` transaction option that prevents operations performed during that transaction's commit from causing the commit to fail. -* Add ``ENABLE_SLOW_TASK_PROFILING`` network option that logs backtraces for long running flow tasks. -* ``getBoundaryKeys`` can be used on locked databases. -* Flow: API versions prior to 500 are no longer supported. -* Flow: ``Cluster::createDatabase`` no longer takes a DB name parameter. -* Node: API versions prior to 500 are no longer supported. -* Node: ``fdb.open`` and ``Cluster.openDatabase`` no longer take a DB name parameter. -* Java: API versions prior to 500 are no longer supported. -* Java: ``FDB.open`` and ``Cluster.openDatabase`` no longer take a DB name parameter. -* Java: Removed ``Transaction.reset`` from the API. -* Java: ``Transaction.onError`` invalidates its ``Transaction`` and asynchronously returns a new ``Transaction`` to replace it. -* Java: Transactions always enable the ``USED_DURING_COMMIT_PROTECTION_DISABLE`` transaction option, preventing operations that occur during a commit from causing the commit to fail. -* Java: There are now options to set the executor for async call backs at the database and transaction level. -* Java: Static functions that perform async operations now have overloads that allow the user to specify an executor. -* Java: Range class now implements equals, toString, and hashCode methods. -* Java: Tuples now expose a "stream" method to get a stream of their objects and "fromStream" to convert streams back into tuples. -* Java: Addressed a pathology that made AsyncUtil.whileTrue susceptible to long chains of futures. - -Other Changes -------------- - -* Added the ``-v``/``--version`` flag to report version information for the ``fdbcli`` binary -* Introduced the ``data_filesystem`` command line argument for the ``fdbserver`` binary to prevent data from being written to the root drive. -* Added a ``ClientStart`` trace event to client trace files with details about the client library being used. -* fdbserver now rejects all unrecognized command-line arguments. -* All fdbserver command-line options now have both short- and long-form equivalents. - -Earlier release notes ---------------------- -* :doc:`4.6 (API Version 460) ` -* :doc:`4.5 (API Version 450) ` -* :doc:`4.4 (API Version 440) ` -* :doc:`4.3 (API Version 430) ` -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +############# +Release Notes +############# + +5.0.8 +===== + +Features +-------- + +* Support upgrades from 3.0 versions. + +Fixes +----- + +* Blob backups didn't retry 429 errors. 429 errors do not count against the retry limit. +* Status was not correctly populating all configuration fields. + +5.0.7 +===== + +Fixes +----- + +* Blob backups became corrupt when handling non-retryable errors. +* Blob backup did not retry all http errors correctly. + +5.0.6 +===== + +5.0.5 +===== + +Fixes +----- + +* Set a default memory limit of 8GB on all backup and DR executables. This limit is configurable on the command line. +* The backup agent would keep attempting to write a file to blob for up to an hour after the task was cancelled. +* Incorrect blob backup destination URLs could be parsed as correct but missing IP addresses. +* Blob load balancing and per address connection limits have been improved. +* Fdbmonitor now supports 0-parameter flags. +* The read latencies reported in status were higher than what clients observed. + +5.0.4 +===== + +Fixes +----- + +* Logs continued to make their data persistent to disk after being removed. +* Removed logs did not delete their data before shutting down. +* In rare scenarios, a disk error which occured during log recruitment could cause the recruitment to hang indefinately. + +5.0.3 +===== + +Fixes +----- + +* In rare scenarios, recovery could get stuck for 10 minutes. +* The consistency check did not work on locked databases. +* In rare scenarios, backup, DR, or fdbcli could hang indefinitely. +* Some transaction log metrics were not being reported. +* Some network metrics were reported incorrectly as extremely large numbers. + +5.0.2 +===== + +Fixes +----- + +* Functionality to slowly delete large files with incremental truncation was not enabled. +* Fixed a source of crashes from fdbcli. +* Data distribution was prematurely reporting that it had started. + +Bindings +-------- + +* Go: Use fully-qualified import paths for fdb dependencies. + +Other +----- + +* Publish header files and static libraries for flow and flow bindings on Linux and macOS. + +5.0.1 +===== + +Fixes +----- + +* Bytes input and bytes durable on the log would drift apart due to rounding errors. + +5.0.0 +===== + +Features +-------- + +* All recoveries no longer copy log data before completion. As a result, the fast_recovery_double and fast_recovery_triple configurations have been removed. +* Added a new configuration ``three_data_hall`` where a single data hall failure cannot bring down a cluster. +* Multiple log processes can be within the same zone. +* Clients have access to sampled latency statistics for their operations. +* Added network checksums. +* Fault tolerance is restored much quicker after a storage server failure. + +Performance +----------- + +* Improved recovery speed after rebooting the cluster. +* Improved saturation performance of write-heavy workloads. +* We no longer require extra log durability for fast recoveries. +* Backup/DR now use far less cluster resources while idle. +* Improved load balancing performance. +* Reduced conflict range sizes when performing get range queries with key selectors such that the resolved begin key is greater than or equal to the resolved end key. +* Added functionality to slowly delete large files with incremental truncation. + +Fixes +----- + +* Fixed a pathology where multiple successive failures could lead to a long 30+ minute availability outage. +* Updated status to have failures of old tlogs included in the failure tolerance calculation. +* The fdbserver and fdbbackup processes could return a successful error code after a fatal error. +* Fault tolerance did not reflect coordinators sharing the same machine ID. +* Prevent the DR seconds behind measurement from potentially returning a negative amount. +* Increased the priority of all cluster controller work to prevent the cluster controller from being starved by other work on the same process. +* Fixed a rare crash in the DR agent. +* fdbcli and fdb_c clients logs had 0 values for most ProcessMetrics log event fields. +* DR could get stuck if the time required to copy range data was longer than the task timeout. + +Status +------ + +* Improved latency probe accuracy when the cluster is loaded. +* Report GRV latencies at all priorities in the latency probe. +* For the SSD storage engine, available disk space now includes space within data files that is not currently in use and can be reused. +* Storage servers report how far they are lagging behind the logs. ``fdbcli`` now reports servers that are lagging sufficiently far behind. +* Status json "incompatible_connections" did not work with multiversion clients. +* Added connection counts and establish/close metrics to status json.  + +Bindings +-------- + +* API version updated to 500. +* Tuples now support single- and double-precision floating point numbers, UUIDs, booleans, and nested tuples. +* Add ``TRANSACTION_LOGGING_ENABLE`` transaction option that causes the details of a transaction's operations to be logged to the client trace logs. +* Add ``USED_DURING_COMMIT_PROTECTION_DISABLE`` transaction option that prevents operations performed during that transaction's commit from causing the commit to fail. +* Add ``ENABLE_SLOW_TASK_PROFILING`` network option that logs backtraces for long running flow tasks. +* ``getBoundaryKeys`` can be used on locked databases. +* Flow: API versions prior to 500 are no longer supported. +* Flow: ``Cluster::createDatabase`` no longer takes a DB name parameter. +* Node: API versions prior to 500 are no longer supported. +* Node: ``fdb.open`` and ``Cluster.openDatabase`` no longer take a DB name parameter. +* Java: API versions prior to 500 are no longer supported. +* Java: ``FDB.open`` and ``Cluster.openDatabase`` no longer take a DB name parameter. +* Java: Removed ``Transaction.reset`` from the API. +* Java: ``Transaction.onError`` invalidates its ``Transaction`` and asynchronously returns a new ``Transaction`` to replace it. +* Java: Transactions always enable the ``USED_DURING_COMMIT_PROTECTION_DISABLE`` transaction option, preventing operations that occur during a commit from causing the commit to fail. +* Java: There are now options to set the executor for async call backs at the database and transaction level. +* Java: Static functions that perform async operations now have overloads that allow the user to specify an executor. +* Java: Range class now implements equals, toString, and hashCode methods. +* Java: Tuples now expose a "stream" method to get a stream of their objects and "fromStream" to convert streams back into tuples. +* Java: Addressed a pathology that made AsyncUtil.whileTrue susceptible to long chains of futures. + +Other Changes +------------- + +* Added the ``-v``/``--version`` flag to report version information for the ``fdbcli`` binary +* Introduced the ``data_filesystem`` command line argument for the ``fdbserver`` binary to prevent data from being written to the root drive. +* Added a ``ClientStart`` trace event to client trace files with details about the client library being used. +* fdbserver now rejects all unrecognized command-line arguments. +* All fdbserver command-line options now have both short- and long-form equivalents. + +Earlier release notes +--------------------- +* :doc:`4.6 (API Version 460) ` +* :doc:`4.5 (API Version 450) ` +* :doc:`4.4 (API Version 440) ` +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/old-release-notes/release-notes-510.rst b/documentation/sphinx/source/old-release-notes/release-notes-510.rst new file mode 100644 index 00000000000..51f4b69d159 --- /dev/null +++ b/documentation/sphinx/source/old-release-notes/release-notes-510.rst @@ -0,0 +1,214 @@ +############# +Release Notes +############# + +5.1.7 +===== + +Fixes +----- + +* fdbdr switch could take a long time to complete if the two clusters were not created at the same time. + +5.1.6 +===== + +Fixes +----- + +* Expiring a backup could cause the fdbbackup process to hang indefinitely. + +5.1.5 +===== + +Fixes +----- + +* The consistency check calculated the size of the database inefficiently. +* Could not create new directories with the Python and Ruby implementations of the directory layer. +* fdbcli could erroneously report that it was incompatible with some processes in the cluster. +* The commit commmand in fdbcli did not wait for the result of the commit before continuing to the next command. + + +Other Changes +------------- + +* renamed the ``multi_dc`` replication mode to ``three_datacenter``. + +5.1.4 +===== + +Fixes +----- + +* The master would recover twice when a new cluster controller was elected. +* The cluster controller could be elected on a storage process after restarting all processes in a cluster. +* Allow backup expiration to succeed if the backup is too new to be restorable. +* Process metric collection in status could sometimes fail. + +5.1.3 +===== + +Fixes +----- + +* The backup agents ran out of memory when heavily loaded. +* Storage servers were not marked as failed until after their files were deleted. +* The consistency check requested too many shards in the same request from the proxy. +* Client knobs for blob send/receive were reversed in meaning. +* fdbbackup status provides more information on reported errors. + +5.1.2 +===== + +Fixes +----- + +* Backup did not incrementally delete mutations from the mutation log. +* fdbcli status misreported completed backup/DR as running. +* Stopped producing the "fdbblob" alias for fdbbackup. + +5.1.1 +===== + +Fixes +----- + +* Bindings: Disposing a transaction during a commit resulted in a broken promise from ``get_versionstamp``. +* Bindings: Calling ``create_cluster`` before initializing the network would result in a crash. +* Latest restorable version of a running backup was not being updated in backup layer status.  +* Backup layer status would sometimes show an error or an incorrect value for the recent blob bandwidth metric. +* Backup deletions were not deleting all of the files related to the backup. +* The cluster controller was sharing a process with the master even when better locations existed. +* Blob credentials files were being opened in read-write mode. +* Sometimes fdbbackup did not write log files even when ``--log`` was passed on the command line. + +Performance +----------- + +* Backup file uploads will respond to server-side throttling in the middle of a chunk upload rather than only between chunks. + +5.1.0 +===== + +Features +-------- + +* Backups continually write snapshots at a configured interval, reducing restore times for long running backups. +* Old backup snapshots and associated logs can be deleted from a backup. +* Backup files are stored in a deep folder structure. +* Restore allows you to specify an approximate time instead of a version. +* Backup and DR agents can be paused from ``fdbbackup`` and ``fdbdr`` respectively. +* Added byte min and byte max atomic operations. +* The behavior of atomic "and" and "min" operations has changed when the key doesn't exist in the database. If the key is not present, then an "and" or "min" is now equivalent to a set. +* Exception messages are more descriptive. +* Clients can view a sample of committed mutations. +* When switching to a DR cluster, the commit versions on that cluster will be higher than the versions on the primary cluster. +* Added a read-only lock aware transaction option. +* Automatically suppress trace log events which occur too frequently. +* Added a new ``multi_dc`` replication mode designed for cross data center deployments. + +Performance +----------- + +* The data distribution algorithm can split the system keyspace. +* Improved load balancing when servers are located across multiple data centers. +* Improved read latencies after recoveries by only making servers responsible for keys if they have finished copying the data from other servers. +* Improved recovery times by waiting until a process has finished recovering its data from disk before letting it be recruited for new roles. +* Improved 95% read version latencies by reducing the number of logs required to confirm that a proxy has not been replaced. +* Stopped the transaction logs from copying unneeded data after multiple successive recoveries. +* Significantly improved the performance of range reads. +* The cluster controller prefers to be recruited on stateless class processes and will not put other stateless roles on the same process. +* Excluded servers no longer take on stateless roles. +* Stateless roles will be proactively moved off of excluded processes. +* Dramatically improved restore speeds of large disk queue files. +* Clients get key location information directly from the proxies, significantly reducing the latency of worst case read patterns. +* Reduced the amount of work incompatible clients generate for coordinators and the cluster controller. In particular, this reduces the load on the cluster caused by using the multi-version client. +* Pop partially recovered mutations from the transaction log to save disk space after multiple successive recoveries. +* Stopped using network checksums when also using TLS. +* Improved cluster performance after recoveries by prioritizing processing new mutations on the logs over copying data from the previous logs. +* Backup agents prefer reading from servers in the same data center. + +Fixes +----- + +* New databases immediately configured into ``three_data_hall`` would not respect the ``three_data_hall`` constraint. +* Exclude considered the free space of non-storage processes when determining if an exclude was safe. +* ``fdbmonitor`` failed to start processes after fork failure. +* ``fdbmonitor`` will only stop processes when the configuration file is deleted if ``kill_on_configuration_change`` is set. +* The data distribution algorithm would hang indefinitely when asked to build storage teams with more than three servers. +* Mutations from a restore could continue to be applied for a very short amount of time after a restore was successfully aborted. + +Extremely Rare Bug Fixes +------------------------ + +* Storage servers did not properly handle rollbacks to versions before their restored version. +* A newly recruited transaction log configured with the memory storage engine could crash on startup. +* The data distribution algorithm could split a key range so that one part did not have any data. +* Storage servers could update to an incorrect version after a master failure. +* The disk queue could report a commit as successful before the sync of the disk queue files completed. +* A disk queue which was shutdown before completing its first commit could become unrecoverable. + +Status +------ + +* If a cluster cannot recover because too many transaction logs are missing, status lists the missing logs. +* The list of connected clients includes their trace log groups. +* Status reports if a cluster is being used as a DR destination. + +Bindings +-------- + +* API version updated to 510. +* Add versionstamp support to the Tuple layer in Java and Python. + +Java +---- + +* API versions prior to 510 are no longer supported. +* The bindings have been moved to the package ``com.apple.foundationdb`` from ``com.apple.cie.foundationdb``. +* We no longer offer a version of the Java bindings with our custom futures library or support Java versions less than 8. The bindings that use completable futures have been renamed to ``fdb-java``. +* Finalizers now log a warning to stderr if an object with native resources is not closed. This can be disabled by calling ``FDB.setUnclosedWarning()``. +* Implementers of the ``Disposable`` interface now implement ``AutoCloseable`` instead, with ``close()`` replacing ``dispose()``. +* ``AutoCloseable`` objects will continue to be closed in object finalizers, but this behavior is being deprecated. All ``AutoCloseable`` objects should be explicitly closed. +* ``AsyncIterator`` is no longer closeable. +* ``getBoundaryKeys()`` now returns a ``CloseableAsyncIterable`` rather than an ``AsyncIterator``. +* ``Transaction.getRange()`` no longer initiates a range read immediately. Instead, the read is issued by a call to ``AsyncIterable.asList()`` or ``AsyncIterable.iterator()``. +* Added ``hashCode()`` method to ``Subspace``. +* Added thread names to threads created by our default executor. +* The network thread by default will be named ``fdb-network-thread``. +* Added an overload of ``whileTrue()`` which takes a ``Supplier``. +* Added experimental support for enabling native callbacks from external threads. +* Fix: Converting the result of ``Transaction.getRange()`` to a list would issue an unneeded range read. +* Fix: range iterators failed to close underlying native resources. +* Fix: various objects internal to the bindings were not properly closed. + +Other Changes +------------- + +* Backups made prior to 5.1 can no longer be restored. +* Backup now uses a hostname in the connection string instead of a list of IPs when backing up to blob storage. This hostname is resolved using DNS. +* ``fdbblob`` functionality has been moved to ``fdbbackup``. +* ``fdbcli`` will warn the user if it is used to connect to an incompatible cluster. +* Cluster files that do not match the current connection string are no longer corrected automatically. +* Improved computation of available memory on pre-3.14 kernels. +* Stopped reporting blob storage connection credentials in ``fdbbackup`` status output. + +Earlier release notes +--------------------- +* :doc:`5.0 (API Version 500) ` +* :doc:`4.6 (API Version 460) ` +* :doc:`4.5 (API Version 450) ` +* :doc:`4.4 (API Version 440) ` +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/priority-queues-java.rst b/documentation/sphinx/source/priority-queues-java.rst index c151e7ca1f8..60d849c9432 100644 --- a/documentation/sphinx/source/priority-queues-java.rst +++ b/documentation/sphinx/source/priority-queues-java.rst @@ -67,20 +67,20 @@ Here's a basic implementation of the model: import java.util.Random; public class MicroPriority { - + private static final FDB fdb; private static final Database db; private static final Subspace pq; private static final Random randno; - + static{ - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); pq = new Subspace(Tuple.from("P")); - + randno = new Random(); } - + public static void push(TransactionContext tcx, final Object value, final int priority){ tcx.run((Transaction tr) -> { byte[] rands = new byte[20]; @@ -90,22 +90,22 @@ Here's a basic implementation of the model: return null; }); } - + private static long nextCount(TransactionContext tcx, final int priority){ return tcx.run((Transaction tr) -> { for(KeyValue kv : tr.snapshot().getRange(pq.subspace(Tuple.from(priority)).range(),1,true)){ return 1l + (long)pq.subspace(Tuple.from(priority)).unpack(kv.getKey()).get(0); } - + return 0l; // None previously with this priority. }); } - + // Pop--assumes min priority queue.. public static Object pop(TransactionContext tcx){ return pop(tcx,false); } - + // Pop--allows for either max or min priority queue. public static Object pop(TransactionContext tcx, final boolean max){ return tcx.run((Transaction tr) -> { @@ -113,16 +113,16 @@ Here's a basic implementation of the model: tr.clear(kv.getKey()); return Tuple.fromBytes(kv.getValue()).get(0); } - + return null; }); } - + // Peek--assumes min priority queue. public static Object peek(TransactionContext tcx){ return peek(tcx,false); } - + // Peek--allows for either max or min priority queue. public static Object peek(TransactionContext tcx, final boolean max){ return tcx.run((Transaction tr) -> { @@ -130,8 +130,8 @@ Here's a basic implementation of the model: for(KeyValue kv : tr.getRange(r.begin, r.end, 1, max)){ return Tuple.fromBytes(kv.getValue()).get(0); } - - return null; + + return null; }); } } diff --git a/documentation/sphinx/source/queues-java.rst b/documentation/sphinx/source/queues-java.rst index c7e067dc5fd..3d5019606d9 100644 --- a/documentation/sphinx/source/queues-java.rst +++ b/documentation/sphinx/source/queues-java.rst @@ -73,12 +73,12 @@ The following is a simple implementation of the basic pattern: private static final Random randno; static{ - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); queue = new Subspace(Tuple.from("Q")); randno = new Random(); } - + // Remove the top element from the queue. public static Object dequeue(TransactionContext tcx){ final KeyValue item = firstItem(tcx); @@ -87,11 +87,11 @@ The following is a simple implementation of the basic pattern: } // Remove from the top of the queue. - tcx.run((Transaction tr) -> + tcx.run((Transaction tr) -> { tr.clear(item.getKey()); return null; }); - + // Return the old value. return Tuple.fromBytes(item.getValue()).get(0); } @@ -101,27 +101,27 @@ The following is a simple implementation of the basic pattern: tcx.run((Transaction tr) -> { byte[] rands = new byte[20]; randno.nextBytes(rands); // Create random seed to avoid conflicts. - tr.set(queue.subspace(Tuple.from(lastIndex(tr)+1, rands)).pack(), + tr.set(queue.subspace(Tuple.from(lastIndex(tr)+1, rands)).pack(), Tuple.from(value).pack()); - + return null; }); } // Get the top element of the queue. private static KeyValue firstItem(TransactionContext tcx){ - return tcx.run((Transaction tr) -> + return tcx.run((Transaction tr) -> { for(KeyValue kv : tr.getRange(queue.range(), 1)){ return kv; } - + return null; // Empty queue. Should never be reached. }); } // Get the last index in the queue. private static long lastIndex(TransactionContext tcx){ - return tcx.run((Transaction tr) -> + return tcx.run((Transaction tr) -> { for(KeyValue kv : tr.snapshot().getRange(queue.range(), 1, true)){ return (long)queue.unpack(kv.getKey()).get(0); } diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index b2aa1e7903f..712e2904481 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -1,214 +1,118 @@ -############# -Release Notes -############# - -5.1.7 -===== - -Fixes ------ - -* fdbdr switch could take a long time to complete if the two clusters were not created at the same time. - -5.1.6 -===== - -Fixes ------ - -* Expiring a backup could cause the fdbbackup process to hang indefinately. - -5.1.5 -===== - -Fixes ------ - -* The consistency check calculated the size of the database inefficiently. -* Could not create new directories with the Python and Ruby implementations of the directory layer. -* fdbcli could erroneously report that it was incompatible with some processes in the cluster. -* The commit commmand in fdbcli did not wait for the result of the commit before continuing to the next command. - - -Other Changes -------------- - -* renamed the ``multi_dc`` replication mode to ``three_datacenter``. - -5.1.4 -===== - -Fixes ------ - -* The master would recover twice when a new cluster controller was elected. -* The cluster controller could be elected on a storage process after restarting all processes in a cluster. -* Allow backup expiration to succeed if the backup is too new to be restorable. -* Process metric collection in status could sometimes fail. - -5.1.3 -===== - -Fixes ------ - -* The backup agents ran out of memory when heavily loaded. -* Storage servers were not marked as failed until after their files were deleted. -* The consistency check requested too many shards in the same request from the proxy. -* Client knobs for blob send/receive were reversed in meaning. -* fdbbackup status provides more information on reported errors. - -5.1.2 -===== - -Fixes ------ - -* Backup did not incrementally delete mutations from the mutation log. -* fdbcli status misreported completed backup/DR as running. -* Stopped producing the "fdbblob" alias for fdbbackup. - -5.1.1 -===== - -Fixes ------ - -* Bindings: Disposing a transaction during a commit resulted in a broken promise from ``get_versionstamp``. -* Bindings: Calling ``create_cluster`` before initializing the network would result in a crash. -* Latest restorable version of a running backup was not being updated in backup layer status.  -* Backup layer status would sometimes show an error or an incorrect value for the recent blob bandwidth metric. -* Backup deletions were not deleting all of the files related to the backup. -* The cluster controller was sharing a process with the master even when better locations existed. -* Blob credentials files were being opened in read-write mode. -* Sometimes fdbbackup did not write log files even when ``--log`` was passed on the command line. - -Performance ------------ - -* Backup file uploads will respond to server-side throttling in the middle of a chunk upload rather than only between chunks. - -5.1.0 -===== - -Features --------- - -* Backups continually write snapshots at a configured interval, reducing restore times for long running backups. -* Old backup snapshots and associated logs can be deleted from a backup. -* Backup files are stored in a deep folder structure. -* Restore allows you to specify an approximate time instead of a version. -* Backup and DR agents can be paused from ``fdbbackup`` and ``fdbdr`` respectively. -* Added byte min and byte max atomic operations. -* The behavior of atomic "and" and "min" operations has changed when the key doesn't exist in the database. If the key is not present, then an "and" or "min" is now equivalent to a set. -* Exception messages are more descriptive. -* Clients can view a sample of committed mutations. -* When switching to a DR cluster, the commit verisons on that cluster will be higher than the versions on the primary cluster. -* Added a read-only lock aware transaction option. -* Automatically suppress trace log events which occur too frequently. -* Added a new ``multi_dc`` replication mode designed for cross data center deployments. - -Performance ------------ - -* The data distribution algorithm can split the system keyspace. -* Improved load balancing when servers are located across multiple data centers. -* Improved read latencies after recoveries by only making servers responsible for keys if they have finished copying the data from other servers. -* Improved recovery times by waiting until a process has finished recovering its data from disk before letting it be recruited for new roles. -* Improved 95% read version latencies by reducing the number of logs required to confirm that a proxy has not been replaced. -* Stopped the transaction logs from copying unneeded data after multiple successive recoveries. -* Significantly improved the performance of range reads. -* The cluster controller prefers to be recruited on stateless class processes and will not put other stateless roles on the same process. -* Excluded servers no longer take on stateless roles. -* Stateless roles will be proactively moved off of excluded processes. -* Dramatically improved restore speeds of large disk queue files. -* Clients get key location information directly from the proxies, sigificantly reducing the latency of worst case read patterns. -* Reduced the amount of work incompatible clients generate for coordinators and the cluster controller. In particular, this reduces the load on the cluster caused by using the multi-version client. -* Pop partially recovered mutations from the transaction log to save disk space after multiple successive recoveries. -* Stopped using network checksums when also using TLS. -* Improved cluster performance after recoveries by prioritizing processing new mutations on the logs over copying data from the previous logs. -* Backup agents prefer reading from servers in the same data center. - -Fixes ------ - -* New databases immediately configured into ``three_data_hall`` would not respect the ``three_data_hall`` constraint. -* Exclude considered the free space of non-storage processes when determining if an exclude was safe. -* ``fdbmonitor`` failed to start processes after fork failure. -* ``fdbmonitor`` will only stop processes when the configuration file is deleted if ``kill_on_configuration_change`` is set. -* The data distribution algorithm would hang indefinately when asked to build storage teams with more than three servers. -* Mutations from a restore could continue to be applied for a very short amount of time after a restore was successfully aborted. - -Extremely Rare Bug Fixes ------------------------- - -* Storage servers did not properly handle rollbacks to versions before their restored version. -* A newly recruited transaction log configured with the memory storage engine could crash on startup. -* The data distribution algorithm could split a key range so that one part did not have any data. -* Storage servers could update to an incorrect version after a master failure. -* The disk queue could report a commit as successful before the sync of the disk queue files completed. -* A disk queue which was shutdown before completing its first commit could become unrecoverable. - -Status ------- - -* If a cluster cannot recover because too many transaction logs are missing, status lists the missing logs. -* The list of connected clients includes their trace log groups. -* Status reports if a cluster is being used as a DR destination. - -Bindings --------- - -* API version updated to 510. -* Add versionstamp support to the Tuple layer in Java and Python. - -Java ----- - -* API versions prior to 510 are no longer supported. -* The bindings have been moved to the package ``com.apple.foundationdb`` from ``com.apple.cie.foundationdb``. -* We no longer offer a version of the Java bindings with our custom futures library or support Java versions less than 8. The bindings that use completable futures have been renamed to ``fdb-java``. -* Finalizers now log a warning to stderr if an object with native resources is not closed. This can be disabled by calling ``FDB.setUnclosedWarning()``. -* Implementers of the ``Disposable`` interface now implement ``AutoCloseable`` instead, with ``close()`` replacing ``dispose()``. -* ``AutoCloseable`` objects will continue to be closed in object finalizers, but this behavior is being deprecated. All ``AutoCloseable`` objects should be explicitly closed. -* ``AsyncIterator`` is no longer closeable. -* ``getBoundaryKeys()`` now returns a ``CloseableAsyncIterable`` rather than an ``AsyncIterator``. -* ``Transaction.getRange()`` no longer initiates a range read immediately. Instead, the read is issued by a call to ``AsyncIterable.asList()`` or ``AsyncIterable.iterator()``. -* Added ``hashCode()`` method to ``Subspace``. -* Added thread names to threads created by our default executor. -* The network thread by default will be named ``fdb-network-thread``. -* Added an overload of ``whileTrue()`` which takes a ``Supplier``. -* Added experimental support for enabling native callbacks from external threads. -* Fix: Converting the result of ``Transaction.getRange()`` to a list would issue an unneeded range read. -* Fix: range iterators failed to close underlying native resources. -* Fix: various objects internal to the bindings were not properly closed. - -Other Changes -------------- - -* Backups made prior to 5.1 can no longer be restored. -* Backup now uses a hostname in the connection string instead of a list of IPs when backing up to blob storage. This hostname is resolved using DNS. -* ``fdbblob`` functionality has been moved to ``fdbbackup``. -* ``fdbcli`` will warn the user if it is used to connect to an incompatible cluster. -* Cluster files that do not match the current connection string are no longer corrected automatically. -* Improved computation of available memory on pre-3.14 kernels. -* Stopped reporting blob storage connection credentials in ``fdbbackup`` status output. - -Earlier release notes ---------------------- -* :doc:`5.0 (API Version 500) ` -* :doc:`4.6 (API Version 460) ` -* :doc:`4.5 (API Version 450) ` -* :doc:`4.4 (API Version 440) ` -* :doc:`4.3 (API Version 430) ` -* :doc:`4.2 (API Version 420) ` -* :doc:`4.1 (API Version 410) ` -* :doc:`4.0 (API Version 400) ` -* :doc:`3.0 (API Version 300) ` -* :doc:`2.0 (API Version 200) ` -* :doc:`1.0 (API Version 100) ` -* :doc:`Beta 3 (API Version 23) ` -* :doc:`Beta 2 (API Version 22) ` -* :doc:`Beta 1 (API Version 21) ` -* :doc:`Alpha 6 (API Version 16) ` -* :doc:`Alpha 5 (API Version 14) ` +############# +Release Notes +############# + +5.2.8 +===== + +Bindings +-------- + +* Java: ``FDBDatabase::run`` and ``FDBDatabase::read`` now use the ``Executor`` provided for executing asynchronous callbacks instead of the default one for the database. `(Issue #640) `_ + +Fixes +----- + +* A large number of concurrent read attempts could bring the database down after a cluster reboot. `(PR #650) `_ + +5.2.7 +===== + +Bindings +-------- + +* The go bindings now caches database connections on a per-cluster basis. `(Issue #607) `_ + +Fixes +----- + +* A client could fail to connect to a cluster when the cluster was upgraded to a version compatible with the client. This affected upgrades that were using the multi-version client to maintain compatibility with both versions of the cluster. `(PR #637) `_ +* Incorrect accounting of incompatible connections led to occasional assertion failures. `(PR #637) `_ + +5.2.6 +===== + +Features +-------- + +* Improved backup error specificity regarding timeouts and active connection failures. `(PR #581) `_ + +Fixes +----- + +* A memory leak was fixed in connection closing. `(PR #574) `_ +* A memory leak was fixed in the coordinator's handling of disconnected clients. `(PR #579) `_ +* Aligned memory allocation on MacOS was sometimes failing to allocate memory, causing a crash. `(PR #547) `_ + +5.2.5 +===== + +Features +-------- + +* Backup and DR share a single mutation log when both are being used on the same cluster. Ongoing backups will be aborted when upgrading to 5.2. `(PR #3) `_ +* Added a TLS plugin implementation. `(PR #343) `_ +* Backup supports HTTPS for blobstore connections. `(PR #343) `_ +* Added the APPEND_IF_FITS atomic operation. `(PR #22) `_ +* Updated the SET_VERSIONSTAMPED_KEY atomic operation to take four bytes to specify the offset instead of two (if the API version is set to 520 or higher). `(Issue #148) `_ +* Updated the SET_VERSIONSTAMPED_VALUE atomic operation to place the versionstamp at a specified offset in a value (if the API version is set to 520 or higher). `(Issue #148) `_ +* tls_verify_peers splits input using the '|' character. [5.2.4] `(PR #468) `_ +* Added knobs and blob Backup URL parameters for operations/sec limits by operation type. [5.2.5] `(PR #513) `_ + +Performance +----------- + +* Improved backup task prioritization. `(PR #71) `_ + +Fixes +----- + +* The client did not clear the storage server interface cache on endpoint failure for all request types. This causes up to one second of additional latency on the first get range request to a rebooted storage server. `(Issue #351) `_ +* Client input validation would handle inputs to versionstamp mutations incorrectly if the API version was less than 520. [5.2.1] `(Issue #387) `_ +* Build would fail on recent versions of Clang. [5.2.2] `(PR #389) `_ +* Clusters running with TLS plugin would reject clients using non-server certificates. [5.2.2] `(PR #396) `_ +* Backup would attempt to clear too many ranges in a single transaction when erasing log ranges. [5.2.3] `(PR #440) `_ +* A read-only transaction using the ``READ_LOCK_AWARE`` option would fail if committed. [5.2.3] `(PR #437) `_ +* fdbcli kill command did not work when TLS was enabled. [5.2.4] `(PR #471) `_ +* Don't disable certificate checks by default. [5.2.5] `(PR #511) `_ + +Status +------ + +* Available space metrics for the memory storage engine take into account both memory and disk. `(PR #41) `_ +* Added metrics for read bytes per second and read keys per second. `(PR #303) `_ + +Bindings +-------- + +* API version updated to 520. +* Java and Python: Versionstamp packing methods within tuple class now add four bytes for the offset instead of two if the API version is set to 520 or higher. `(Issue #148) `_ +* Added convenience methods to determine if an API version has been set. `(PR #72) `_ +* Go: Reduce memory allocations when packing tuples. `(PR #278) `_ +* Python: Correctly thread the versionstamp offset when there are incomplete versionstamps within nested tuples. `(Issue #356) `_ +* Java: Length in ``Tuple.fromBytes`` is now honored if specified. `(Issue #362) `_ + +Other Changes +------------- + +* Deprecated the read_ahead_disable option. The commit_on_first_proxy, debug_dump, and check_writes_enable options are no longer exposed through the bindings. `(PR #134) `_ + +Earlier release notes +--------------------- +* :doc:`5.1 (API Version 510) ` +* :doc:`5.0 (API Version 500) ` +* :doc:`4.6 (API Version 460) ` +* :doc:`4.5 (API Version 450) ` +* :doc:`4.4 (API Version 440) ` +* :doc:`4.3 (API Version 430) ` +* :doc:`4.2 (API Version 420) ` +* :doc:`4.1 (API Version 410) ` +* :doc:`4.0 (API Version 400) ` +* :doc:`3.0 (API Version 300) ` +* :doc:`2.0 (API Version 200) ` +* :doc:`1.0 (API Version 100) ` +* :doc:`Beta 3 (API Version 23) ` +* :doc:`Beta 2 (API Version 22) ` +* :doc:`Beta 1 (API Version 21) ` +* :doc:`Alpha 6 (API Version 16) ` +* :doc:`Alpha 5 (API Version 14) ` diff --git a/documentation/sphinx/source/simple-indexes-java.rst b/documentation/sphinx/source/simple-indexes-java.rst index 5859003be8e..42ff4431e8e 100644 --- a/documentation/sphinx/source/simple-indexes-java.rst +++ b/documentation/sphinx/source/simple-indexes-java.rst @@ -85,24 +85,24 @@ In this example, we’re storing user data based on user ID but sometimes need t private static final Database db; private static final Subspace main; private static final Subspace index; - + static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); main = new Subspace(Tuple.from("user")); index = new Subspace(Tuple.from("zipcode_index")); } - + // TODO These three methods (setUser, getUser, and getUserIDsInRegion) // are all in the recipe book. public static void setUser(TransactionContext tcx, final String ID, final String name, final String zipcode){ - tcx.run(tr -> + tcx.run(tr -> tr.set(main.pack(Tuple.from(ID,zipcode)), Tuple.from(name).pack()); tr.set(index.pack(Tuple.from(zipcode,ID)), Tuple.from().pack()); return null; }); } - + // Normal lookup. public static String getUser(TransactionContext tcx, final String ID){ return tcx.run(tr -> { @@ -113,7 +113,7 @@ In this example, we’re storing user data based on user ID but sometimes need t return ""; }); } - + // Index lookup. public static ArrayList getUserIDsInRegion(TransactionContext tcx, final String zipcode){ return tcx.run(tr -> { diff --git a/documentation/sphinx/source/tables-java.rst b/documentation/sphinx/source/tables-java.rst index d1d8774bb4f..f5e3648bc26 100644 --- a/documentation/sphinx/source/tables-java.rst +++ b/documentation/sphinx/source/tables-java.rst @@ -60,36 +60,36 @@ Here’s a simple implementation of the basic table pattern: private static final Subspace table; private static final Subspace rowIndex; private static final Subspace colIndex; - + static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); table = new Subspace(Tuple.from("T")); rowIndex = table.subspace(Tuple.from("R")); colIndex = table.subspace(Tuple.from("C")); } - + // Packing and unpacking helper functions. private static byte[] pack(Object value){ return Tuple.from(value).pack(); } - + private static Object unpack(byte[] value){ return Tuple.fromBytes(value).get(0); } - - public static void setCell(TransactionContext tcx, final String row, + + public static void setCell(TransactionContext tcx, final String row, final String column, final Object value){ tcx.run(tr -> { tr.set(rowIndex.subspace(Tuple.from(row, column)).getKey(), pack(value)); - tr.set(colIndex.subspace(Tuple.from(column,row)).getKey(), + tr.set(colIndex.subspace(Tuple.from(column,row)).getKey(), pack(value)); - + return null; }); } - + public static Object getCell(TransactionContext tcx, final String row, final String column){ return tcx.run(tr -> { @@ -97,19 +97,19 @@ Here’s a simple implementation of the basic table pattern: Tuple.from(row,column)).getKey()).get()); }); } - + public static void setRow(TransactionContext tcx, final String row, final Map cols){ tcx.run(tr -> { tr.clear(rowIndex.subspace(Tuple.from(row)).range()); - + for(Map.Entry cv : cols.entrySet()){ setCell(tr, row, cv.getKey(), cv.getValue()); } return null; }); } - + public static void setColumn(TransactionContext tcx, final String column, final Map rows){ tcx.run(tr -> { @@ -120,37 +120,37 @@ Here’s a simple implementation of the basic table pattern: return null; }); } - + public static TreeMap getRow(TransactionContext tcx, final String row){ return tcx.run(tr -> { TreeMap cols = new TreeMap(); - + for(KeyValue kv : tr.getRange( rowIndex.subspace(Tuple.from(row)).range())){ cols.put(rowIndex.unpack(kv.getKey()).getString(1), unpack(kv.getValue())); } - + return cols; }); } - - + + public static TreeMap getColumn(TransactionContext tcx, final String column){ return tcx.run(tr -> { TreeMap rows = new TreeMap(); - + for(KeyValue kv : tr.getRange( colIndex.subspace(Tuple.from(column)).range())){ rows.put(colIndex.unpack(kv.getKey()).getString(1), unpack(kv.getValue())); } - + return rows; }); } } -That’s about all you need to store and retrieve data from simple tables. \ No newline at end of file +That’s about all you need to store and retrieve data from simple tables. diff --git a/documentation/sphinx/source/time-series.rst b/documentation/sphinx/source/time-series.rst index 51021b7617f..33d6e3720b6 100644 --- a/documentation/sphinx/source/time-series.rst +++ b/documentation/sphinx/source/time-series.rst @@ -18,7 +18,7 @@ We have a number of customers, ranging from network analytics providers to VoIP/ Time-series Data and FoundationDB ================================= -If you only have a few fields to store per database record, it’s pretty simple to picture what a time-oriented record would look like. If you were tracking analytics for a website, you might have a few fields, such as a website identifer, a page identfier, and a browser type. That’s pretty simple to picture fitting into a defined, relational-database table that looks like this: +If you only have a few fields to store per database record, it’s pretty simple to picture what a time-oriented record would look like. If you were tracking analytics for a website, you might have a few fields, such as a website identifier, a page identifier, and a browser type. That’s pretty simple to picture fitting into a defined, relational-database table that looks like this: ============= ========== ======= ========== Timestamp website_ID page_ID browser_ID diff --git a/documentation/sphinx/source/tls.rst b/documentation/sphinx/source/tls.rst index 7e3e5dc0dec..6921bcb121a 100644 --- a/documentation/sphinx/source/tls.rst +++ b/documentation/sphinx/source/tls.rst @@ -20,9 +20,9 @@ Setting Up FoundationDB to use TLS Enabling TLS in a new cluster ----------------------------- -To set a new cluster to use TLS, use the ``-t`` flag on ``make-public.py``:: +To set a new cluster to use TLS, use the ``-t`` flag on ``make_public.py``:: - user@host1$ sudo /usr/lib/foundationdb/make-public.py -t + user@host1$ sudo /usr/lib/foundationdb/make_public.py -t /etc/foundationdb/fdb.cluster is now using address 10.0.1.1 (TLS enabled) This will configure the new cluster to communicate with TLS. @@ -59,17 +59,21 @@ Command-line Option Client Option Environment Variable Purpo key, used by the plugin ``tls_verify_peers`` ``TLS_verify_peers`` ``FDB_TLS_VERIFY_PEERS`` The byte-string for the verification of peer certificates and sessions, used by the plugin +``tls_password`` ``TLS_password`` ``FDB_TLS_PASSWORD`` The byte-string representing the passcode for + unencrypting the private key +``tls_ca_file`` ``TLS_ca_path`` ``FDB_TLS_CA_FILE`` Path to the file containing the CA certificates + to trust ======================== ==================== ============================ ================================================== The value for each setting can be specified in more than one way. The actual valued used is determined in the following order: -1. An explicity specified value as a command-line option or client option, if one is given; +1. An explicitly specified value as a command-line option or client option, if one is given; 2. The value of the environment variable, if one has been set; 3. The default value As with all other command-line options to ``fdbserver``, the TLS settings can be specified in the :ref:`[fdbserver] section of the configuration file `. -The settings for certificate file, key file, and peer verification are interpreted by the loaded plugin. +The settings for certificate file, key file, peer verification, password and CA file are interpreted by the loaded plugin. Default Values -------------- @@ -79,9 +83,9 @@ Plugin default location Similarly, if a value is not specified for the parameter ``tls_plugin``, the file will be specified by the environment variable ``FDB_TLS_PLUGIN`` or, if this variable is not set, the system-dependent location: - * Linux: ``/usr/lib/foundationdb/plugins/FDBLibTLS.so`` - * macOS: ``/usr/local/foundationdb/plugins/FDBLibTLS.dylib`` - * Windows: ``C:\Program Files\foundationdb\plugins\FDBLibTLS.dll`` + * Linux: ``/usr/lib/foundationdb/plugins/fdb-libressl-plugin.so`` + * macOS: ``/usr/local/foundationdb/plugins/fdb-libressl-plugin.dylib`` + * Windows: ``C:\Program Files\foundationdb\plugins\fdb-libressl-plugin.dll`` On Windows, this location will be relative to the chosen installation location. The environment variable ``FOUNDATIONDB_INSTALL_PATH`` will be used in place of ``C:\Program Files\foundationdb\`` to determine this location. @@ -97,7 +101,17 @@ The default behavior when the certificate or key file is not specified is to loo Default Peer Verification ^^^^^^^^^^^^^^^^^^^^^^^^^ -The default peer verification is the empty string. +The default peer verification is ``Check.Valid=1``. + +Default Password +^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no default password. If no password is specified, the plugin assumes that private key is unencrypted. + +CA file default location +^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a value is not specified, the plugin searches for certs in the default openssl certs location. Parameters and client bindings ------------------------------ @@ -132,7 +146,7 @@ A file must be supplied that contains an ordered list of certificates. The first All but the last certificate are provided to peers during TLS handshake as the certificate chain. -The last certificate in the list is the trusted certificate. All processes that want to communicate must have the same trusted certificate. +The last certificate in the list is the trusted certificate. .. note:: If the certificate list contains only one certificate, that certificate *must* be self-signed and will be used as both the certificate chain and the trusted certificate. @@ -153,6 +167,8 @@ The key file must contain the private key corresponding to the process' own cert xxxxxxxxxxxxxxx -----END PRIVATE KEY----- +It can optionally be encrypted by the password provided to tls_password. + Certificate creation -------------------- @@ -173,7 +189,7 @@ A FoundationDB server or client will only communicate with peers that present a Certificate field verification ------------------------------ -With a peer verification string, FoundationDB servers and clients can adjust what is required of the certificate chain presented by a peer. These options can make the certificate requirements more rigorous or more lenient. +With a peer verification string, FoundationDB servers and clients can adjust what is required of the certificate chain presented by a peer. These options can make the certificate requirements more rigorous or more lenient. You can specify multiple verification strings by providing additional tls_verify_peers command line arguments or concatenating them with ``|``. All ``,`` or ``|`` in the verify peers fields should be escaped with ``\``. Turning down the validation ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,18 +199,18 @@ If the default checking of the certificate chain is too stringent, the verificat ===================== ============================================================= Setting Result ===================== ============================================================= -``Check.Valid=0`` Sets the current process to disable all further verification +``Check.Valid=0`` Sets the current process to disable all further verification of a peer certificate. -``Check.Unexpired=0`` Disables date checking of peer certificates. If the clocks in - the cluster and between the clients and servers are not to be - trusted, setting this value to ``0`` can allow communications +``Check.Unexpired=0`` Disables date checking of peer certificates. If the clocks in + the cluster and between the clients and servers are not to be + trusted, setting this value to ``0`` can allow communications to proceed. ===================== ============================================================= Adding verification requirements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Requirements can be placed on the fields of the Issuer and Subject DNs in the peer's own certificate. These reqirements take the form of a comma-separated list of conditions. Each condition takes the form of ``field=value``. Only certain fields from a DN can be matched against. +Requirements can be placed on the fields of the Issuer and Subject DNs in the peer's own certificate. These requirements take the form of a comma-separated list of conditions. Each condition takes the form of ``field=value``. Only certain fields from a DN can be matched against. ====== =================== Field Well known name @@ -216,6 +232,8 @@ Prefix DN ``I.``, or ``Issuer.`` Issuer ============================= ======== +Additionally, the verification can be restricted to certificates signed by a given root CA with the field ``Root.CN``. This allows you to have different requirements for different root chains. + The value of a condition must be specified in a form derived from a subset of `RFC 4514 `_. Specifically, the "raw" notation (a value starting with the ``#`` character) is not accepted. Other escaping mechanisms, including specifying characters by hex notation, are allowed. The specified field's value must exactly match the value in the peer's certificate. By default, the fields of a peer certificate's DNs are not examined. diff --git a/documentation/sphinx/source/vector-java.rst b/documentation/sphinx/source/vector-java.rst index 075af3ba817..1ffd47c5e88 100644 --- a/documentation/sphinx/source/vector-java.rst +++ b/documentation/sphinx/source/vector-java.rst @@ -75,20 +75,20 @@ Here’s the basic pattern: private static final FDB fdb; private static final Database db; private static final Subspace vector; - + static { - fdb = FDB.selectAPIVersion(510); + fdb = FDB.selectAPIVersion(520); db = fdb.open(); vector = new Subspace(Tuple.from("V")); } - + public static Object get(TransactionContext tcx, final long index){ return tcx.run(tr -> { return Tuple.fromBytes(tr.get(vector.pack( Tuple.from(index))).get()).get(0); }); } - + public static void set(TransactionContext tcx, final long index, final Object value){ tcx.run(tr -> { tr.set(vector.pack(Tuple.from(index)), Tuple.from(value).pack()); diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 865442c9ada..850d96061ca 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -2078,6 +2078,8 @@ struct CLIOptions { std::string tlsCertPath; std::string tlsKeyPath; std::string tlsVerifyPeers; + std::string tlsCAPath; + std::string tlsPassword; CLIOptions( int argc, char* argv[] ) : trace(false), @@ -2151,9 +2153,15 @@ struct CLIOptions { case TLSOptions::OPT_TLS_CERTIFICATES: tlsCertPath = args.OptionArg(); break; + case TLSOptions::OPT_TLS_CA_FILE: + tlsCAPath = args.OptionArg(); + break; case TLSOptions::OPT_TLS_KEY: tlsKeyPath = args.OptionArg(); break; + case TLSOptions::OPT_TLS_PASSWORD: + tlsPassword = args.OptionArg(); + break; case TLSOptions::OPT_TLS_VERIFY_PEERS: tlsVerifyPeers = args.OptionArg(); break; @@ -2574,7 +2582,8 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { if (tokens.size() == 1) { Standalone kvs = wait( makeInterruptable( tr->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces"), LiteralStringRef("\xff\xff\xff")), 1) ) ); for( auto it : kvs ) { - address_interface[it.key] = it.value; + auto ip_port = it.key.endsWith(LiteralStringRef(":tls")) ? it.key.removeSuffix(LiteralStringRef(":tls")) : it.key; + address_interface[ip_port] = it.value; } } if (tokens.size() == 1 || tokencmp(tokens[1], "list")) { @@ -2710,7 +2719,8 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { LiteralStringRef("\xff\xff\xff")), 1))); for (const auto& pair : kvs) { - printf("%s\n", printable(pair.key).c_str()); + auto ip_port = pair.key.endsWith(LiteralStringRef(":tls")) ? pair.key.removeSuffix(LiteralStringRef(":tls")) : pair.key; + printf("%s\n", printable(ip_port).c_str()); } continue; } @@ -2742,7 +2752,8 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { state std::vector all_profiler_addresses; state std::vector>> all_profiler_responses; for (const auto& pair : kvs) { - interfaces.emplace(pair.key, BinaryReader::fromStringRef(pair.value, IncludeVersion())); + auto ip_port = pair.key.endsWith(LiteralStringRef(":tls")) ? pair.key.removeSuffix(LiteralStringRef(":tls")) : pair.key; + interfaces.emplace(ip_port, BinaryReader::fromStringRef(pair.value, IncludeVersion())); } if (tokens.size() == 6 && tokencmp(tokens[5], "all")) { for (const auto& pair : interfaces) { @@ -3177,8 +3188,20 @@ int main(int argc, char **argv) { return 1; } } + if (opt.tlsCAPath.size()) { + try { + setNetworkOption(FDBNetworkOptions::TLS_CA_PATH, opt.tlsCAPath); + } + catch (Error& e) { + fprintf(stderr, "ERROR: cannot set TLS CA path to `%s' (%s)\n", opt.tlsCAPath.c_str(), e.what()); + return 1; + } + } if ( opt.tlsKeyPath.size() ) { try { + if (opt.tlsPassword.size()) + setNetworkOption(FDBNetworkOptions::TLS_PASSWORD, opt.tlsPassword); + setNetworkOption(FDBNetworkOptions::TLS_KEY_PATH, opt.tlsKeyPath); } catch( Error& e ) { fprintf(stderr, "ERROR: cannot set TLS key path to `%s' (%s)\n", opt.tlsKeyPath.c_str(), e.what()); diff --git a/fdbclient/Atomic.h b/fdbclient/Atomic.h index 105c9c9f8f4..816aaf7bc3a 100644 --- a/fdbclient/Atomic.h +++ b/fdbclient/Atomic.h @@ -227,14 +227,14 @@ static KeyRangeRef getVersionstampKeyRange(Arena& arena, const KeyRef &key, cons KeyRef begin(arena, key); KeyRef end(arena, key); - if (begin.size() < 2) + if (begin.size() < 4) throw client_invalid_operation(); - int16_t pos; - memcpy(&pos, begin.end() - sizeof(int16_t), sizeof(int16_t)); - pos = littleEndian16(pos); - begin = begin.substr(0, begin.size() - 2); - end = end.substr(0, end.size() - 1); + int32_t pos; + memcpy(&pos, begin.end() - sizeof(int32_t), sizeof(int32_t)); + pos = littleEndian32(pos); + begin = begin.substr(0, begin.size() - 4); + end = end.substr(0, end.size() - 3); mutateString(end)[end.size()-1] = 0; if (pos < 0 || pos + 10 > begin.size()) @@ -255,28 +255,19 @@ static void placeVersionstamp( uint8_t* destination, Version version, uint16_t t memcpy( destination + sizeof(version), &transactionNumber, sizeof(transactionNumber) ); } -static void transformSetVersionstampedKey( MutationRef& mutation, Version version, uint16_t transactionNumber ) { - // This transforms a SetVersionstampedKey mutation into a SetValue mutation. - // It is the responsibility of the caller to also add a write conflict range for the new mutation's key. - if (mutation.param1.size() >= 2) { - int16_t pos; - memcpy(&pos, mutation.param1.end() - sizeof(int16_t), sizeof(int16_t)); - pos = littleEndian16(pos); - mutation.param1 = mutation.param1.substr(0, mutation.param1.size() - 2); - - if (pos >= 0 && pos + 10 <= mutation.param1.size()) { - placeVersionstamp( mutateString(mutation.param1) + pos, version, transactionNumber ); +static void transformVersionstampMutation( MutationRef& mutation, StringRef MutationRef::* param, Version version, uint16_t transactionNumber ) { + if ((mutation.*param).size() >= 4) { + int32_t pos; + memcpy(&pos, (mutation.*param).end() - sizeof(int32_t), sizeof(int32_t)); + pos = littleEndian32(pos); + mutation.*param = (mutation.*param).substr(0, (mutation.*param).size() - 4); + + if (pos >= 0 && pos + 10 <= (mutation.*param).size()) { + placeVersionstamp( mutateString(mutation.*param) + pos, version, transactionNumber ); } } mutation.type = MutationRef::SetValue; } -static void transformSetVersionstampedValue( MutationRef& mutation, Version version, uint16_t transactionNumber ) { - if (mutation.param2.size() >= 10) - placeVersionstamp( mutateString(mutation.param2), version, transactionNumber ); - - mutation.type = MutationRef::SetValue; -} - -#endif \ No newline at end of file +#endif diff --git a/fdbclient/BackupAgent.h b/fdbclient/BackupAgent.h index 788fa585108..2d971586a9c 100644 --- a/fdbclient/BackupAgent.h +++ b/fdbclient/BackupAgent.h @@ -56,6 +56,7 @@ class BackupAgentBase : NonCopyable { static const Key keyLastUid; static const Key keyBeginKey; static const Key keyEndKey; + static const Key keyDrVersion; static const Key destUid; static const Key backupStartVersion; @@ -347,7 +348,7 @@ class DatabaseBackupAgent : public BackupAgentBase { return runRYWTransaction(cx, [=](Reference tr){ return discontinueBackup(tr, tagName); }); } - Future abortBackup(Database cx, Key tagName, bool partial = false); + Future abortBackup(Database cx, Key tagName, bool partial = false, bool abortOldBackup = false); Future getStatus(Database cx, int errorLimit, Key tagName); @@ -373,12 +374,14 @@ class DatabaseBackupAgent : public BackupAgentBase { // will return when the backup directory is restorable. Future waitBackup(Database cx, Key tagName, bool stopWhenDone = true); Future waitSubmitted(Database cx, Key tagName); + Future waitUpgradeToLatestDrVersion(Database cx, Key tagName); static const Key keyAddPrefix; static const Key keyRemovePrefix; static const Key keyRangeVersions; static const Key keyCopyStop; static const Key keyDatabasesInSync; + static const int LATEST_DR_VERSION; Future getTaskCount(Reference tr) { return taskBucket->getTaskCount(tr); } Future getTaskCount(Database cx) { return taskBucket->getTaskCount(cx); } @@ -420,7 +423,7 @@ bool copyParameter(Reference source, Reference dest, Key key); Version getVersionFromString(std::string const& value); Standalone> getLogRanges(Version beginVersion, Version endVersion, Key destUidValue, int blockSize = CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE); Standalone> getApplyRanges(Version beginVersion, Version endVersion, Key backupUid); -Future eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional beginVersion = Optional(), Optional endVersion = Optional(), bool checkBackupUid = false, Version backupUid = 0); +Future eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional endVersion = Optional(), bool checkBackupUid = false, Version backupUid = 0); Key getApplyKey( Version version, Key backupUid ); std::pair decodeBKMutationLogKey(Key key); Standalone> decodeBackupLogValue(StringRef value); diff --git a/fdbclient/BackupAgentBase.actor.cpp b/fdbclient/BackupAgentBase.actor.cpp index f9ecfa20c20..bdc3f3a7d16 100644 --- a/fdbclient/BackupAgentBase.actor.cpp +++ b/fdbclient/BackupAgentBase.actor.cpp @@ -35,6 +35,7 @@ const Key BackupAgentBase::keyStateStatus = LiteralStringRef("state_status"); const Key BackupAgentBase::keyLastUid = LiteralStringRef("last_uid"); const Key BackupAgentBase::keyBeginKey = LiteralStringRef("beginKey"); const Key BackupAgentBase::keyEndKey = LiteralStringRef("endKey"); +const Key BackupAgentBase::keyDrVersion = LiteralStringRef("drVersion"); const Key BackupAgentBase::destUid = LiteralStringRef("destUid"); const Key BackupAgentBase::backupStartVersion = LiteralStringRef("backupStartVersion"); @@ -623,146 +624,112 @@ ACTOR Future applyMutations(Database cx, Key uid, Key addPrefix, Key remov } } -ACTOR Future _clearLogRanges(Reference tr, bool clearVersionHistory, Key logUidValue, Key destUidValue, Version beginVersion, Version endVersion) { +ACTOR static Future _eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional endVersion, bool checkBackupUid, Version backupUid) { state Key backupLatestVersionsPath = destUidValue.withPrefix(backupLatestVersionsPrefix); state Key backupLatestVersionsKey = logUidValue.withPrefix(backupLatestVersionsPath); - tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - tr->setOption(FDBTransactionOptions::LOCK_AWARE); - - state Standalone backupVersions = wait(tr->getRange(KeyRangeRef(backupLatestVersionsPath, strinc(backupLatestVersionsPath)), CLIENT_KNOBS->TOO_MANY)); - - // Make sure version history key does exist and lower the beginVersion if needed - bool foundSelf = false; - for (auto backupVersion : backupVersions) { - Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue); - - if (currLogUidValue == logUidValue) { - foundSelf = true; - beginVersion = std::min(beginVersion, BinaryReader::fromStringRef(backupVersion.value, Unversioned())); - } - } - // Do not clear anything if version history key cannot be found - if (!foundSelf) { + if (!destUidValue.size()) { return Void(); } - Version nextSmallestVersion = endVersion; - bool clearLogRangesRequired = true; - - // More than one backup/DR with the same range - if (backupVersions.size() > 1) { - for (auto backupVersion : backupVersions) { - Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue); - Version currVersion = BinaryReader::fromStringRef(backupVersion.value, Unversioned()); + state Reference tr(new ReadYourWritesTransaction(cx)); + loop{ + try { + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - if (currLogUidValue == logUidValue) { - continue; - } else if (currVersion > beginVersion) { - nextSmallestVersion = std::min(currVersion, nextSmallestVersion); - } else { - // If we can find a version less than or equal to beginVersion, clearing log ranges is not required - clearLogRangesRequired = false; - break; + if (checkBackupUid) { + Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue); + Optional v = wait( tr->get( sourceStates.pack(DatabaseBackupAgent::keyFolderId) ) ); + if(v.present() && BinaryReader::fromStringRef(v.get(), Unversioned()) > backupUid) + return Void(); } - } - } - - if (clearVersionHistory && backupVersions.size() == 1) { - // Clear version history - tr->clear(prefixRange(backupLatestVersionsPath)); - // Clear everything under blog/[destUid] - tr->clear(prefixRange(destUidValue.withPrefix(backupLogKeys.begin))); + state Standalone backupVersions = wait(tr->getRange(KeyRangeRef(backupLatestVersionsPath, strinc(backupLatestVersionsPath)), CLIENT_KNOBS->TOO_MANY)); - // Disable committing mutations into blog - tr->clear(prefixRange(destUidValue.withPrefix(logRangesRange.begin))); - } else { - if (clearVersionHistory) { - // Clear current backup version history - tr->clear(backupLatestVersionsKey); - } else { - // Update current backup latest version - tr->set(backupLatestVersionsKey, BinaryWriter::toValue(endVersion, Unversioned())); - } + // Make sure version history key does exist and lower the beginVersion if needed + state Version currBeginVersion = invalidVersion; + for (auto backupVersion : backupVersions) { + Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue); - // Clear log ranges if needed - if (clearLogRangesRequired) { - Standalone> ranges = getLogRanges(beginVersion, nextSmallestVersion, destUidValue); - for (auto& range : ranges) { - tr->clear(range); + if (currLogUidValue == logUidValue) { + currBeginVersion = BinaryReader::fromStringRef(backupVersion.value, Unversioned()); + break; + } } - } - } - - return Void(); -} -// The difference between beginVersion and endVersion should not be too large -Future clearLogRanges(Reference tr, bool clearVersionHistory, Key logUidValue, Key destUidValue, Version beginVersion, Version endVersion) { - return _clearLogRanges(tr, clearVersionHistory, logUidValue, destUidValue, beginVersion, endVersion); -} + // Do not clear anything if version history key cannot be found + if (currBeginVersion == invalidVersion) { + return Void(); + } -ACTOR static Future _eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional beginVersion, Optional endVersion, bool checkBackupUid, Version backupUid) { - if ((beginVersion.present() && endVersion.present() && endVersion.get() <= beginVersion.get()) || !destUidValue.size()) - return Void(); + state Version currEndVersion = currBeginVersion + CLIENT_KNOBS->CLEAR_LOG_RANGE_COUNT * CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE; + if(endVersion.present()) { + currEndVersion = std::min(currEndVersion, endVersion.get()); + } - state Version currBeginVersion; - state Version endVersionValue; - state Version currEndVersion; - state bool clearVersionHistory; - - ASSERT(beginVersion.present() == endVersion.present()); - if (beginVersion.present()) { - currBeginVersion = beginVersion.get(); - endVersionValue = endVersion.get(); - clearVersionHistory = false; - } else { - // If beginVersion and endVersion are not presented, it means backup is done and we need to clear version history. - // Set currBeginVersion to INTMAX_MAX and it will be set to the correct version in clearLogRanges(). - // Set endVersionValue to INTMAX_MAX since we need to clear log ranges up to next smallest version. - currBeginVersion = endVersionValue = currEndVersion = INTMAX_MAX; - clearVersionHistory = true; - } + state Version nextSmallestVersion = currEndVersion; + bool clearLogRangesRequired = true; + + // More than one backup/DR with the same range + if (backupVersions.size() > 1) { + for (auto backupVersion : backupVersions) { + Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue); + Version currVersion = BinaryReader::fromStringRef(backupVersion.value, Unversioned()); + + if (currLogUidValue == logUidValue) { + continue; + } else if (currVersion > currBeginVersion) { + nextSmallestVersion = std::min(currVersion, nextSmallestVersion); + } else { + // If we can find a version less than or equal to beginVersion, clearing log ranges is not required + clearLogRangesRequired = false; + break; + } + } + } + if (!endVersion.present() && backupVersions.size() == 1) { + // Clear version history + tr->clear(prefixRange(backupLatestVersionsPath)); - while (currBeginVersion < endVersionValue || clearVersionHistory) { - state Reference tr(new ReadYourWritesTransaction(cx)); - - loop{ - try { - tr->setOption(FDBTransactionOptions::LOCK_AWARE); - tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - - if (checkBackupUid) { - Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue); - Optional v = wait( tr->get( sourceStates.pack(DatabaseBackupAgent::keyFolderId) ) ); - if(v.present() && BinaryReader::fromStringRef(v.get(), Unversioned()) > backupUid) - return Void(); - } + // Clear everything under blog/[destUid] + tr->clear(prefixRange(destUidValue.withPrefix(backupLogKeys.begin))); - if (!clearVersionHistory) { - currEndVersion = std::min(currBeginVersion + CLIENT_KNOBS->CLEAR_LOG_RANGE_COUNT * CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE, endVersionValue); + // Disable committing mutations into blog + tr->clear(prefixRange(destUidValue.withPrefix(logRangesRange.begin))); + } else { + if (!endVersion.present() && currEndVersion >= nextSmallestVersion) { + // Clear current backup version history + tr->clear(backupLatestVersionsKey); + } else { + // Update current backup latest version + tr->set(backupLatestVersionsKey, BinaryWriter::toValue(currEndVersion, Unversioned())); } - Void _ = wait(clearLogRanges(tr, clearVersionHistory, logUidValue, destUidValue, currBeginVersion, currEndVersion)); - Void _ = wait(tr->commit()); - - if (clearVersionHistory) { - return Void(); + // Clear log ranges if needed + if (clearLogRangesRequired) { + Standalone> ranges = getLogRanges(currBeginVersion, nextSmallestVersion, destUidValue); + for (auto& range : ranges) { + tr->clear(range); + } } + } + Void _ = wait(tr->commit()); - currBeginVersion = currEndVersion; - break; - } catch (Error &e) { - Void _ = wait(tr->onError(e)); + if (!endVersion.present() && (backupVersions.size() == 1 || currEndVersion >= nextSmallestVersion)) { + return Void(); } + if(endVersion.present() && currEndVersion == endVersion.get()) { + return Void(); + } + tr->reset(); + } catch (Error &e) { + Void _ = wait(tr->onError(e)); } } - - return Void(); } -Future eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional beginVersion, Optional endVersion, bool checkBackupUid, Version backupUid) { - return _eraseLogData(cx, logUidValue, destUidValue, beginVersion, endVersion, checkBackupUid, backupUid); -} \ No newline at end of file +Future eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional endVersion, bool checkBackupUid, Version backupUid) { + return _eraseLogData(cx, logUidValue, destUidValue, endVersion, checkBackupUid, backupUid); +} diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index 52ae312ee06..70fd60fd9c3 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -33,6 +33,7 @@ const Key DatabaseBackupAgent::keyRemovePrefix = LiteralStringRef("remove_prefix const Key DatabaseBackupAgent::keyRangeVersions = LiteralStringRef("range_versions"); const Key DatabaseBackupAgent::keyCopyStop = LiteralStringRef("copy_stop"); const Key DatabaseBackupAgent::keyDatabasesInSync = LiteralStringRef("databases_in_sync"); +const int DatabaseBackupAgent::LATEST_DR_VERSION = 1; DatabaseBackupAgent::DatabaseBackupAgent() : subspace(Subspace(databaseBackupPrefixRange.begin)) @@ -411,7 +412,7 @@ namespace dbBackup { } }; - StringRef BackupRangeTaskFunc::name = LiteralStringRef("db_backup_range"); + StringRef BackupRangeTaskFunc::name = LiteralStringRef("dr_backup_range"); const uint32_t BackupRangeTaskFunc::version = 1; const Key BackupRangeTaskFunc::keyAddBackupRangeTasks = LiteralStringRef("addBackupRangeTasks"); const Key BackupRangeTaskFunc::keyBackupRangeBeginKey = LiteralStringRef("backupRangeBeginKey"); @@ -457,7 +458,7 @@ namespace dbBackup { Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef FinishFullBackupTaskFunc::name = LiteralStringRef("db_finish_full_backup"); + StringRef FinishFullBackupTaskFunc::name = LiteralStringRef("dr_finish_full_backup"); const uint32_t FinishFullBackupTaskFunc::version = 1; REGISTER_TASKFUNC(FinishFullBackupTaskFunc); @@ -475,21 +476,20 @@ namespace dbBackup { Void _ = wait(checkTaskVersion(cx, task, EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version)); - Version beginVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyBeginVersion], Unversioned()); Version endVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyEndVersion], Unversioned()); - Void _ = wait(eraseLogData(taskBucket->src, task->params[BackupAgentBase::keyConfigLogUid], task->params[BackupAgentBase::destUid], Optional(beginVersion), Optional(endVersion), true, BinaryReader::fromStringRef(task->params[BackupAgentBase::keyFolderId], Unversioned()))); + Void _ = wait(eraseLogData(taskBucket->src, task->params[BackupAgentBase::keyConfigLogUid], task->params[BackupAgentBase::destUid], Optional(endVersion), true, BinaryReader::fromStringRef(task->params[BackupAgentBase::keyFolderId], Unversioned()))); return Void(); } - ACTOR static Future addTask(Reference tr, Reference taskBucket, Reference parentTask, Version beginVersion, Version endVersion, TaskCompletionKey completionKey, Reference waitFor = Reference()) { + ACTOR static Future addTask(Reference tr, Reference taskBucket, Reference parentTask, Version endVersion, TaskCompletionKey completionKey, Reference waitFor = Reference()) { Key doneKey = wait(completionKey.get(tr, taskBucket)); Reference task(new Task(EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version, doneKey, 1)); copyDefaultParameters(parentTask, task); - task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned()); + task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(1, Unversioned()); //FIXME: remove in 6.X, only needed for 5.2 backward compatibility task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned()); if (!waitFor) { @@ -508,7 +508,7 @@ namespace dbBackup { return Void(); } }; - StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("db_erase_log_range"); + StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("dr_erase_log_range"); const uint32_t EraseLogRangeTaskFunc::version = 1; REGISTER_TASKFUNC(EraseLogRangeTaskFunc); @@ -685,7 +685,7 @@ namespace dbBackup { return Void(); } }; - StringRef CopyLogRangeTaskFunc::name = LiteralStringRef("db_copy_log_range"); + StringRef CopyLogRangeTaskFunc::name = LiteralStringRef("dr_copy_log_range"); const uint32_t CopyLogRangeTaskFunc::version = 1; const Key CopyLogRangeTaskFunc::keyNextBeginVersion = LiteralStringRef("nextBeginVersion"); REGISTER_TASKFUNC(CopyLogRangeTaskFunc); @@ -748,7 +748,7 @@ namespace dbBackup { // Do not erase at the first time if (prevBeginVersion > 0) { - addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, prevBeginVersion, beginVersion, TaskCompletionKey::joinWith(allPartsDone))); + addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, beginVersion, TaskCompletionKey::joinWith(allPartsDone))); } Void _ = wait(waitForAll(addTaskVector) && taskBucket->finish(tr, task)); @@ -788,7 +788,7 @@ namespace dbBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Void(); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef CopyLogsTaskFunc::name = LiteralStringRef("db_copy_logs"); + StringRef CopyLogsTaskFunc::name = LiteralStringRef("dr_copy_logs"); const uint32_t CopyLogsTaskFunc::version = 1; REGISTER_TASKFUNC(CopyLogsTaskFunc); @@ -855,7 +855,7 @@ namespace dbBackup { } Version backupUid = BinaryReader::fromStringRef(task->params[BackupAgentBase::keyFolderId], Unversioned()); - Void _ = wait(eraseLogData(taskBucket->src, logUidValue, destUidValue, Optional(), Optional(), true, backupUid)); + Void _ = wait(eraseLogData(taskBucket->src, logUidValue, destUidValue, Optional(), true, backupUid)); return Void(); } @@ -900,7 +900,7 @@ namespace dbBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef FinishedFullBackupTaskFunc::name = LiteralStringRef("db_finished_full_backup"); + StringRef FinishedFullBackupTaskFunc::name = LiteralStringRef("dr_finished_full_backup"); const uint32_t FinishedFullBackupTaskFunc::version = 1; const Key FinishedFullBackupTaskFunc::keyInsertTask = LiteralStringRef("insertTask"); REGISTER_TASKFUNC(FinishedFullBackupTaskFunc); @@ -951,7 +951,7 @@ namespace dbBackup { } if (prevBeginVersion > 0) { - addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, prevBeginVersion, beginVersion, TaskCompletionKey::joinWith(allPartsDone))); + addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, beginVersion, TaskCompletionKey::joinWith(allPartsDone))); } Void _ = wait(waitForAll(addTaskVector) && taskBucket->finish(tr, task)); @@ -983,10 +983,381 @@ namespace dbBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Void(); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef CopyDiffLogsTaskFunc::name = LiteralStringRef("db_copy_diff_logs"); + StringRef CopyDiffLogsTaskFunc::name = LiteralStringRef("dr_copy_diff_logs"); const uint32_t CopyDiffLogsTaskFunc::version = 1; REGISTER_TASKFUNC(CopyDiffLogsTaskFunc); + // Skip unneeded EraseLogRangeTaskFunc in 5.1 + struct SkipOldEraseLogRangeTaskFunc : TaskFuncBase { + static StringRef name; + static const uint32_t version; + + ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { + state Reference taskFuture = futureBucket->unpack(task->params[Task::reservedTaskParamKeyDone]); + Void _ = wait(taskFuture->set(tr, taskBucket) && taskBucket->finish(tr, task)); + return Void(); + } + + StringRef getName() const { return name; }; + + Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Void(); }; + Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; + }; + StringRef SkipOldEraseLogRangeTaskFunc::name = LiteralStringRef("dr_skip_legacy_task"); + const uint32_t SkipOldEraseLogRangeTaskFunc::version = 1; + REGISTER_TASKFUNC(SkipOldEraseLogRangeTaskFunc); + REGISTER_TASKFUNC_ALIAS(SkipOldEraseLogRangeTaskFunc, db_erase_log_range); + + // This is almost the same as CopyLogRangeTaskFunc in 5.1. The only purpose is to support DR upgrade + struct OldCopyLogRangeTaskFunc : TaskFuncBase { + static StringRef name; + static const uint32_t version; + + static struct { + static TaskParam bytesWritten() { return LiteralStringRef(__FUNCTION__); } + } Params; + + static const Key keyNextBeginVersion; + + StringRef getName() const { return name; }; + + Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; + Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; + + ACTOR static Future dumpData(Database cx, Reference task, PromiseStream results, FlowLock* lock, Reference tb) { + state bool endOfStream = false; + state Subspace conf = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keyConfig).get(task->params[BackupAgentBase::keyConfigLogUid]); + + state std::vector> nextMutations; + state int64_t nextMutationSize = 0; + loop{ + try { + if (endOfStream && !nextMutationSize) { + return Void(); + } + + state std::vector> mutations = std::move(nextMutations); + state int64_t mutationSize = nextMutationSize; + nextMutations = std::vector>(); + nextMutationSize = 0; + + if (!endOfStream) { + loop{ + try { + RCGroup group = waitNext(results.getFuture()); + lock->release(group.items.expectedSize()); + + int vecSize = group.items.expectedSize(); + if (mutationSize + vecSize >= CLIENT_KNOBS->BACKUP_LOG_WRITE_BATCH_MAX_SIZE) { + + nextMutations.push_back(group.items); + nextMutationSize = vecSize; + break; + } + + mutations.push_back(group.items); + mutationSize += vecSize; + } + catch (Error &e) { + state Error error = e; + if (e.code() == error_code_end_of_stream) { + endOfStream = true; + break; + } + + throw error; + } + } + } + + state Transaction tr(cx); + + loop{ + try { + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + tr.options.customTransactionSizeLimit = 2 * CLIENT_KNOBS->TRANSACTION_SIZE_LIMIT; + Void _ = wait(checkDatabaseLock(&tr, BinaryReader::fromStringRef(task->params[BackupAgentBase::keyConfigLogUid], Unversioned()))); + state int64_t bytesSet = 0; + + bool first = true; + for(auto m : mutations) { + for(auto kv : m) { + if(first) { + tr.addReadConflictRange(singleKeyRange(kv.key)); + first = false; + } + tr.set(kv.key.removePrefix(backupLogKeys.begin).withPrefix(applyLogKeys.begin), kv.value); + bytesSet += kv.expectedSize() - backupLogKeys.begin.expectedSize() + applyLogKeys.begin.expectedSize(); + } + } + + Void _ = wait(tr.commit()); + Params.bytesWritten().set(task, Params.bytesWritten().getOrDefault(task) + bytesSet); + break; + } + catch (Error &e) { + Void _ = wait(tr.onError(e)); + } + } + } + catch (Error &e) { + if (e.code() == error_code_actor_cancelled || e.code() == error_code_backup_error) + throw e; + + state Error err = e; + Void _ = wait(logError(cx, Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keyErrors).pack(task->params[BackupAgentBase::keyConfigLogUid]), format("ERROR: Failed to dump mutations because of error %s", err.what()))); + + throw err; + } + } + } + + ACTOR static Future _execute(Database cx, Reference taskBucket, Reference futureBucket, Reference task) { + state Reference lock(new FlowLock(CLIENT_KNOBS->BACKUP_LOCK_BYTES)); + + Void _ = wait(checkTaskVersion(cx, task, OldCopyLogRangeTaskFunc::name, OldCopyLogRangeTaskFunc::version)); + + state Version beginVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyBeginVersion], Unversioned()); + state Version endVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyEndVersion], Unversioned()); + state Version newEndVersion = std::min(endVersion, (((beginVersion-1) / CLIENT_KNOBS->BACKUP_BLOCK_SIZE) + 2 + (g_network->isSimulated() ? CLIENT_KNOBS->BACKUP_SIM_COPY_LOG_RANGES : 0)) * CLIENT_KNOBS->BACKUP_BLOCK_SIZE); + + state Standalone> ranges = getLogRanges(beginVersion, newEndVersion, task->params[BackupAgentBase::keyConfigLogUid], CLIENT_KNOBS->BACKUP_BLOCK_SIZE); + state std::vector> results; + state std::vector> rc; + state std::vector> dump; + + for (int i = 0; i < ranges.size(); ++i) { + results.push_back(PromiseStream()); + rc.push_back(readCommitted(taskBucket->src, results[i], Future(Void()), lock, ranges[i], decodeBKMutationLogKey, true, true, true)); + dump.push_back(dumpData(cx, task, results[i], lock.getPtr(), taskBucket)); + } + + Void _ = wait(waitForAll(dump)); + + if (newEndVersion < endVersion) { + task->params[OldCopyLogRangeTaskFunc::keyNextBeginVersion] = BinaryWriter::toValue(newEndVersion, Unversioned()); + } + + return Void(); + } + + ACTOR static Future addTask(Reference tr, Reference taskBucket, Reference parentTask, Version beginVersion, Version endVersion, TaskCompletionKey completionKey, Reference waitFor = Reference()) { + Key doneKey = wait(completionKey.get(tr, taskBucket)); + Reference task(new Task(OldCopyLogRangeTaskFunc::name, OldCopyLogRangeTaskFunc::version, doneKey, 1)); + + copyDefaultParameters(parentTask, task); + + task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned()); + task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned()); + + if (!waitFor) { + return taskBucket->addTask(tr, task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId]); + } + + Void _ = wait(waitFor->onSetAddTask(tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); + return LiteralStringRef("OnSetAddTask"); + } + + ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { + + state Version beginVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyBeginVersion], Unversioned()); + state Version endVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyEndVersion], Unversioned()); + state Reference taskFuture = futureBucket->unpack(task->params[Task::reservedTaskParamKeyDone]); + + // Get the bytesWritten parameter from task and atomically add it to the logBytesWritten() property of the DR config. + DRConfig config(task); + int64_t bytesWritten = Params.bytesWritten().getOrDefault(task); + config.logBytesWritten().atomicOp(tr, bytesWritten, MutationRef::AddValue); + + if (task->params.find(OldCopyLogRangeTaskFunc::keyNextBeginVersion) != task->params.end()) { + state Version nextVersion = BinaryReader::fromStringRef(task->params[OldCopyLogRangeTaskFunc::keyNextBeginVersion], Unversioned()); + Void _ = wait(success(OldCopyLogRangeTaskFunc::addTask(tr, taskBucket, task, nextVersion, endVersion, TaskCompletionKey::signal(taskFuture->key))) && taskBucket->finish(tr, task)); + } + else { + Void _ = wait(taskFuture->set(tr, taskBucket) && taskBucket->finish(tr, task)); + } + + return Void(); + } + }; + StringRef OldCopyLogRangeTaskFunc::name = LiteralStringRef("db_copy_log_range"); + const uint32_t OldCopyLogRangeTaskFunc::version = 1; + const Key OldCopyLogRangeTaskFunc::keyNextBeginVersion = LiteralStringRef("nextBeginVersion"); + REGISTER_TASKFUNC(OldCopyLogRangeTaskFunc); + + struct AbortOldBackupTaskFunc : TaskFuncBase { + static StringRef name; + static const uint32_t version; + + ACTOR static Future _execute(Database cx, Reference taskBucket, Reference futureBucket, Reference task) { + state DatabaseBackupAgent srcDrAgent(taskBucket->src); + state Reference tr(new ReadYourWritesTransaction(cx)); + state Key tagNameKey; + + loop { + try { + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + Key tagPath = srcDrAgent.states.get(task->params[DatabaseBackupAgent::keyConfigLogUid]).pack(BackupAgentBase::keyConfigBackupTag); + Optional tagName = wait(tr->get(tagPath)); + if (!tagName.present()) { + return Void(); + } + + tagNameKey = tagName.get(); + break; + } catch (Error &e) { + Void _ = wait(tr->onError(e)); + } + } + + TraceEvent("DBA_abort_old_backup").detail("tagName", tagNameKey.printable()); + Void _ = wait(srcDrAgent.abortBackup(cx, tagNameKey, false, true)); + + return Void(); + } + + ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { + Void _ = wait(taskBucket->finish(tr, task)); + return Void(); + } + + ACTOR static Future addTask(Reference tr, Reference taskBucket, Reference parentTask, TaskCompletionKey completionKey, Reference waitFor = Reference()) { + Key doneKey = wait(completionKey.get(tr, taskBucket)); + Reference task(new Task(AbortOldBackupTaskFunc::name, AbortOldBackupTaskFunc::version, doneKey, 1)); + + copyDefaultParameters(parentTask, task); + + if (!waitFor) { + return taskBucket->addTask(tr, task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId]); + } + + Void _ = wait(waitFor->onSetAddTask(tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); + return LiteralStringRef("OnSetAddTask"); + } + + StringRef getName() const { return name; }; + + Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; + Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; + }; + StringRef AbortOldBackupTaskFunc::name = LiteralStringRef("dr_abort_legacy_backup"); + const uint32_t AbortOldBackupTaskFunc::version = 1; + REGISTER_TASKFUNC(AbortOldBackupTaskFunc); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_backup_range); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_finish_full_backup); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_copy_logs); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_finished_full_backup); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_backup_restorable); + REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_start_full_backup); + + //Upgrade DR from 5.1 + struct CopyDiffLogsUpgradeTaskFunc : TaskFuncBase { + static StringRef name; + static const uint32_t version; + + ACTOR static Future _execute(Database cx, Reference taskBucket, Reference futureBucket, Reference task) { + state Key logUidValue = task->params[DatabaseBackupAgent::keyConfigLogUid]; + state Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue); + state Subspace config = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keyConfig).get(logUidValue); + Void _ = wait(checkTaskVersion(cx, task, CopyDiffLogsUpgradeTaskFunc::name, CopyDiffLogsUpgradeTaskFunc::version)); + + // Retrieve backupRanges + state Standalone> backupRanges; + state Reference tr(new ReadYourWritesTransaction(cx)); + loop { + try { + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + Future verified = taskBucket->keepRunning(tr, task); + Void _ = wait(verified); + + Optional backupKeysPacked = wait(tr->get(config.pack(BackupAgentBase::keyConfigBackupRanges))); + if (!backupKeysPacked.present()) { + return Void(); + } + + BinaryReader br(backupKeysPacked.get(), IncludeVersion()); + br >> backupRanges; + break; + } catch(Error &e) { + Void _ = wait(tr->onError(e)); + } + } + + // Set destUidValue and versionKey on src side + state Key destUidValue(logUidValue); + state Reference srcTr(new ReadYourWritesTransaction(taskBucket->src)); + loop { + try { + srcTr->setOption(FDBTransactionOptions::LOCK_AWARE); + srcTr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + + state Optional v = wait( srcTr->get( sourceStates.pack(DatabaseBackupAgent::keyFolderId) ) ); + if(v.present() && BinaryReader::fromStringRef(v.get(), Unversioned()) > BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyFolderId], Unversioned())) { + return Void(); + } + + if (backupRanges.size() == 1) { + state Key destUidLookupPath = BinaryWriter::toValue(backupRanges[0], IncludeVersion()).withPrefix(destUidLookupPrefix); + Optional existingDestUidValue = wait(srcTr->get(destUidLookupPath)); + if (existingDestUidValue.present()) { + if (destUidValue == existingDestUidValue.get()) { + // due to unknown commit result + break; + } else { + // existing backup/DR is running + return Void(); + } + } + + srcTr->set(destUidLookupPath, destUidValue); + } + + Key versionKey = logUidValue.withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix); + srcTr->set(versionKey, task->params[DatabaseBackupAgent::keyBeginVersion]); + Void _ = wait(srcTr->commit()); + break; + } catch(Error &e) { + Void _ = wait(srcTr->onError(e)); + } + } + + task->params[BackupAgentBase::destUid] = destUidValue; + ASSERT(destUidValue == logUidValue); + + return Void(); + } + + + ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { + Void _ = wait(checkTaskVersion(tr, task, CopyDiffLogsUpgradeTaskFunc::name, CopyDiffLogsUpgradeTaskFunc::version)); + state Reference onDone = futureBucket->unpack(task->params[Task::reservedTaskParamKeyDone]); + + if (task->params[BackupAgentBase::destUid].size() == 0) { + TraceEvent("DBA_CopyDiffLogsUpgradeTaskFunc_abort_in_upgrade"); + Key _ = wait(AbortOldBackupTaskFunc::addTask(tr, taskBucket, task, TaskCompletionKey::signal(onDone))); + } else { + Version beginVersion = BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyBeginVersion], Unversioned()); + Subspace config = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keyConfig).get(task->params[DatabaseBackupAgent::keyConfigLogUid]); + tr->set(config.pack(BackupAgentBase::destUid), task->params[BackupAgentBase::destUid]); + tr->set(config.pack(BackupAgentBase::keyDrVersion), BinaryWriter::toValue(DatabaseBackupAgent::LATEST_DR_VERSION, Unversioned())); + Key _ = wait(CopyDiffLogsTaskFunc::addTask(tr, taskBucket, task, 0, beginVersion, TaskCompletionKey::signal(onDone))); + } + + Void _ = wait(taskBucket->finish(tr, task)); + return Void(); + } + + StringRef getName() const { return name; }; + + Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; + Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; + }; + StringRef CopyDiffLogsUpgradeTaskFunc::name = LiteralStringRef("db_copy_diff_logs"); + const uint32_t CopyDiffLogsUpgradeTaskFunc::version = 1; + REGISTER_TASKFUNC(CopyDiffLogsUpgradeTaskFunc); + struct BackupRestorableTaskFunc : TaskFuncBase { static StringRef name; static const uint32_t version; @@ -1075,7 +1446,7 @@ namespace dbBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef BackupRestorableTaskFunc::name = LiteralStringRef("db_backup_restorable"); + StringRef BackupRestorableTaskFunc::name = LiteralStringRef("dr_backup_restorable"); const uint32_t BackupRestorableTaskFunc::version = 1; REGISTER_TASKFUNC(BackupRestorableTaskFunc); @@ -1087,16 +1458,14 @@ namespace dbBackup { state Key logUidValue = task->params[DatabaseBackupAgent::keyConfigLogUid]; state Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue); Void _ = wait(checkTaskVersion(cx, task, StartFullBackupTaskFunc::name, StartFullBackupTaskFunc::version)); - state Key destUidValue(logUidValue); - state UID logUid = BinaryReader::fromStringRef(logUidValue, Unversioned()); state Standalone> backupRanges = BinaryReader::fromStringRef>>(task->params[DatabaseBackupAgent::keyConfigBackupRanges], IncludeVersion()); state Key beginVersionKey; state Reference srcTr(new ReadYourWritesTransaction(taskBucket->src)); loop { - try { + try { srcTr->setOption(FDBTransactionOptions::LOCK_AWARE); srcTr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); @@ -1251,16 +1620,46 @@ namespace dbBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef StartFullBackupTaskFunc::name = LiteralStringRef("db_start_full_backup"); + StringRef StartFullBackupTaskFunc::name = LiteralStringRef("dr_start_full_backup"); const uint32_t StartFullBackupTaskFunc::version = 1; REGISTER_TASKFUNC(StartFullBackupTaskFunc); - } class DatabaseBackupAgentImpl { public: static const int MAX_RESTORABLE_FILE_METASECTION_BYTES = 1024 * 8; + ACTOR static Future waitUpgradeToLatestDrVersion(DatabaseBackupAgent* backupAgent, Database cx, Key tagName) { + state UID logUid = wait(backupAgent->getLogUid(cx, tagName)); + state Key drVersionKey = backupAgent->config.get(BinaryWriter::toValue(logUid, Unversioned())).pack(DatabaseBackupAgent::keyDrVersion); + + TraceEvent("DRU_watchLatestDrVersion").detail("drVersionKey", drVersionKey.printable()).detail("logUid", BinaryWriter::toValue(logUid, Unversioned()).printable()); + + loop { + state Reference tr(new ReadYourWritesTransaction(cx)); + + loop { + try { + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + Optional drVersion = wait(tr->get(drVersionKey)); + + TraceEvent("DRU_versionCheck").detail("current", drVersion.present() ? BinaryReader::fromStringRef(drVersion.get(), Unversioned()) : -1).detail("expected", DatabaseBackupAgent::LATEST_DR_VERSION).detail("logUid", BinaryWriter::toValue(logUid, Unversioned()).printable()); + if (drVersion.present() && BinaryReader::fromStringRef(drVersion.get(), Unversioned()) == DatabaseBackupAgent::LATEST_DR_VERSION) { + return Void(); + } + + state Future watchDrVersionFuture = tr->watch(drVersionKey); + Void _ = wait(tr->commit()); + Void _ = wait(watchDrVersionFuture); + break; + } catch (Error &e) { + Void _ = wait(tr->onError(e)); + } + } + } + } + // This method will return the final status of the backup ACTOR static Future waitBackup(DatabaseBackupAgent* backupAgent, Database cx, Key tagName, bool stopWhenDone) { state std::string backTrace; @@ -1387,6 +1786,7 @@ class DatabaseBackupAgentImpl { // Clear DRConfig for this UID, which unfortunately only contains some newer vars and not the stuff below. DRConfig(logUid).clear(tr); + tr->set(backupAgent->config.get(logUidValue).pack(DatabaseBackupAgent::keyDrVersion), BinaryWriter::toValue(DatabaseBackupAgent::LATEST_DR_VERSION, Unversioned())); tr->set(backupAgent->config.get(logUidValue).pack(DatabaseBackupAgent::keyAddPrefix), addPrefix); tr->set(backupAgent->config.get(logUidValue).pack(DatabaseBackupAgent::keyRemovePrefix), removePrefix); tr->set(backupAgent->states.get(logUidValue).pack(DatabaseBackupAgent::keyConfigBackupTag), tagName); @@ -1574,7 +1974,7 @@ class DatabaseBackupAgentImpl { return Void(); } - ACTOR static Future abortBackup(DatabaseBackupAgent* backupAgent, Database cx, Key tagName, bool partial) { + ACTOR static Future abortBackup(DatabaseBackupAgent* backupAgent, Database cx, Key tagName, bool partial, bool abortOldBackup) { state Reference tr(new ReadYourWritesTransaction(cx)); state Key logUidValue, destUidValue; state UID logUid, destUid; @@ -1685,6 +2085,14 @@ class DatabaseBackupAgentImpl { break; } + if (abortOldBackup) { + srcTr->set( backupAgent->sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_ABORTED) )); + srcTr->set( backupAgent->sourceStates.get(logUidValue).pack(DatabaseBackupAgent::keyFolderId), backupUid ); + srcTr->clear(prefixRange(logUidValue.withPrefix(backupLogKeys.begin))); + srcTr->clear(prefixRange(logUidValue.withPrefix(logRangesRange.begin))); + break; + } + Key latestVersionKey = logUidValue.withPrefix(destUidValue.withPrefix(backupLatestVersionsPrefix)); Optional bVersion = wait(srcTr->get(latestVersionKey)); @@ -1700,7 +2108,7 @@ class DatabaseBackupAgentImpl { Void _ = wait(srcTr->commit()); endVersion = srcTr->getCommittedVersion() + 1; - + break; } catch (Error &e) { @@ -1708,7 +2116,7 @@ class DatabaseBackupAgentImpl { } } - if (clearSrcDb) { + if (clearSrcDb && !abortOldBackup) { Void _ = wait(eraseLogData(backupAgent->taskBucket->src, logUidValue, destUidValue)); } @@ -1899,8 +2307,8 @@ Future DatabaseBackupAgent::discontinueBackup(Reference DatabaseBackupAgent::abortBackup(Database cx, Key tagName, bool partial){ - return DatabaseBackupAgentImpl::abortBackup(this, cx, tagName, partial); +Future DatabaseBackupAgent::abortBackup(Database cx, Key tagName, bool partial, bool abortOldBackup){ + return DatabaseBackupAgentImpl::abortBackup(this, cx, tagName, partial, abortOldBackup); } Future DatabaseBackupAgent::getStatus(Database cx, int errorLimit, Key tagName) { @@ -1919,6 +2327,10 @@ Future DatabaseBackupAgent::getLogUid(Reference return DatabaseBackupAgentImpl::getLogUid(this, tr, tagName); } +Future DatabaseBackupAgent::waitUpgradeToLatestDrVersion(Database cx, Key tagName) { + return DatabaseBackupAgentImpl::waitUpgradeToLatestDrVersion(this, cx, tagName); +} + Future DatabaseBackupAgent::waitBackup(Database cx, Key tagName, bool stopWhenDone) { return DatabaseBackupAgentImpl::waitBackup(this, cx, tagName, stopWhenDone); } diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 3208b50ef14..05bebeba3d4 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -94,6 +94,7 @@ static std::string describe( std::set const& items, int max_items = -1 ) { } std::string printable( const StringRef& val ); +std::string printable( const std::string& val ); std::string printable( const Optional& val ); std::string printable( const Optional>& val ); std::string printable( const KeyRangeRef& range ); diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index 826b34fdc0a..29392083fe6 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -717,7 +717,7 @@ namespace fileBackup { return Void(); } - ACTOR static Future abortOldBackup(FileBackupAgent* backupAgent, Reference tr, std::string tagName) { + ACTOR static Future abortFiveZeroBackup(FileBackupAgent* backupAgent, Reference tr, std::string tagName) { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); @@ -761,7 +761,7 @@ namespace fileBackup { return Void(); } - struct AbortOldBackupTask : TaskFuncBase { + struct AbortFiveZeroBackupTask : TaskFuncBase { static StringRef name; ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { state FileBackupAgent backupAgent; @@ -772,14 +772,14 @@ namespace fileBackup { TraceEvent(SevInfo, "FileBackupCancelOldTask") .detail("task", printable(task->params[Task::reservedTaskParamKeyType])) .detail("tagName", tagName); - Void _ = wait(abortOldBackup(&backupAgent, tr, tagName)); + Void _ = wait(abortFiveZeroBackup(&backupAgent, tr, tagName)); Void _ = wait(taskBucket->finish(tr, task)); return Void(); } virtual StringRef getName() const { - TraceEvent(SevError, "FileBackupError").detail("cause", "AbortOldBackupTaskFunc::name() should never be called"); + TraceEvent(SevError, "FileBackupError").detail("cause", "AbortFiveZeroBackupTaskFunc::name() should never be called"); ASSERT(false); return StringRef(); } @@ -787,16 +787,86 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Future(Void()); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef AbortOldBackupTask::name = LiteralStringRef("abort_legacy_backup"); - REGISTER_TASKFUNC(AbortOldBackupTask); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_backup_diff_logs); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_backup_log_range); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_backup_logs); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_backup_range); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_backup_restorable); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_finish_full_backup); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_finished_full_backup); - REGISTER_TASKFUNC_ALIAS(AbortOldBackupTask, file_start_full_backup); + StringRef AbortFiveZeroBackupTask::name = LiteralStringRef("abort_legacy_backup"); + REGISTER_TASKFUNC(AbortFiveZeroBackupTask); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_diff_logs); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_log_range); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_logs); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_range); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_restorable); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_finish_full_backup); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_finished_full_backup); + REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_start_full_backup); + + ACTOR static Future abortFiveOneBackup(FileBackupAgent* backupAgent, Reference tr, std::string tagName) { + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + + state KeyBackedTag tag = makeBackupTag(tagName); + state UidAndAbortedFlagT current = wait(tag.getOrThrow(tr, false, backup_unneeded())); + + state BackupConfig config(current.first); + EBackupState status = wait(config.stateEnum().getD(tr, EBackupState::STATE_NEVERRAN)); + + if (!backupAgent->isRunnable((BackupAgentBase::enumState)status)) { + throw backup_unneeded(); + } + + TraceEvent(SevInfo, "FBA_abortFileOneBackup") + .detail("tagName", tagName.c_str()) + .detail("status", BackupAgentBase::getStateText(status)); + + // Cancel backup task through tag + Void _ = wait(tag.cancel(tr)); + + Key configPath = uidPrefixKey(logRangesRange.begin, config.getUid()); + Key logsPath = uidPrefixKey(backupLogKeys.begin, config.getUid()); + + tr->clear(KeyRangeRef(configPath, strinc(configPath))); + tr->clear(KeyRangeRef(logsPath, strinc(logsPath))); + + config.stateEnum().set(tr, EBackupState::STATE_ABORTED); + + return Void(); + } + + struct AbortFiveOneBackupTask : TaskFuncBase { + static StringRef name; + ACTOR static Future _finish(Reference tr, Reference taskBucket, Reference futureBucket, Reference task) { + state FileBackupAgent backupAgent; + state BackupConfig config(task); + state std::string tagName = wait(config.tag().getOrThrow(tr)); + + TEST(true); // Canceling 5.1 backup task + + TraceEvent(SevInfo, "FileBackupCancelFiveOneTask") + .detail("task", printable(task->params[Task::reservedTaskParamKeyType])) + .detail("tagName", tagName); + Void _ = wait(abortFiveOneBackup(&backupAgent, tr, tagName)); + + Void _ = wait(taskBucket->finish(tr, task)); + return Void(); + } + + virtual StringRef getName() const { + TraceEvent(SevError, "FileBackupError").detail("cause", "AbortFiveOneBackupTaskFunc::name() should never be called"); + ASSERT(false); + return StringRef(); + } + + Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Future(Void()); }; + Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; + }; + StringRef AbortFiveOneBackupTask::name = LiteralStringRef("abort_legacy_backup_5.2"); + REGISTER_TASKFUNC(AbortFiveOneBackupTask); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_write_range); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_dispatch_ranges); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_write_logs); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_erase_logs); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_dispatch_logs); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_finished); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_write_snapshot_manifest); + REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_start); std::function)> NOP_SETUP_TASK_FN = [](Reference task) { /* NOP */ }; ACTOR static Future addBackupTask(StringRef name, @@ -1137,7 +1207,7 @@ namespace fileBackup { } }; - StringRef BackupRangeTaskFunc::name = LiteralStringRef("file_backup_write_range"); + StringRef BackupRangeTaskFunc::name = LiteralStringRef("file_backup_write_range_5.2"); const uint32_t BackupRangeTaskFunc::version = 1; REGISTER_TASKFUNC(BackupRangeTaskFunc); @@ -1620,7 +1690,7 @@ namespace fileBackup { } }; - StringRef BackupSnapshotDispatchTask::name = LiteralStringRef("file_backup_dispatch_ranges"); + StringRef BackupSnapshotDispatchTask::name = LiteralStringRef("file_backup_dispatch_ranges_5.2"); const uint32_t BackupSnapshotDispatchTask::version = 1; REGISTER_TASKFUNC(BackupSnapshotDispatchTask); @@ -1816,7 +1886,7 @@ namespace fileBackup { } }; - StringRef BackupLogRangeTaskFunc::name = LiteralStringRef("file_backup_write_logs"); + StringRef BackupLogRangeTaskFunc::name = LiteralStringRef("file_backup_write_logs_5.2"); const uint32_t BackupLogRangeTaskFunc::version = 1; REGISTER_TASKFUNC(BackupLogRangeTaskFunc); @@ -1841,30 +1911,25 @@ namespace fileBackup { state Reference lock(new FlowLock(CLIENT_KNOBS->BACKUP_LOCK_BYTES)); Void _ = wait(checkTaskVersion(cx, task, EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version)); - state Version beginVersion = Params.beginVersion().get(task); state Version endVersion = Params.endVersion().get(task); state Key destUidValue = Params.destUidValue().get(task); state BackupConfig config(task); state Key logUidValue = config.getUidAsKey(); - if (beginVersion == 0) { - Void _ = wait(eraseLogData(cx, logUidValue, destUidValue)); - } else { - Void _ = wait(eraseLogData(cx, logUidValue, destUidValue, Optional(beginVersion), Optional(endVersion))); - } + Void _ = wait(eraseLogData(cx, logUidValue, destUidValue, endVersion != 0 ? Optional(endVersion) : Optional())); return Void(); } - ACTOR static Future addTask(Reference tr, Reference taskBucket, UID logUid, TaskCompletionKey completionKey, Key destUidValue, Version beginVersion = 0, Version endVersion = 0, Reference waitFor = Reference()) { + ACTOR static Future addTask(Reference tr, Reference taskBucket, UID logUid, TaskCompletionKey completionKey, Key destUidValue, Version endVersion = 0, Reference waitFor = Reference()) { Key key = wait(addBackupTask(EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version, tr, taskBucket, completionKey, BackupConfig(logUid), waitFor, [=](Reference task) { - Params.beginVersion().set(task, beginVersion); + Params.beginVersion().set(task, 1); //FIXME: remove in 6.X, only needed for 5.2 backward compatibility Params.endVersion().set(task, endVersion); Params.destUidValue().set(task, destUidValue); }, @@ -1885,7 +1950,7 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("file_backup_erase_logs"); + StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("file_backup_erase_logs_5.2"); const uint32_t EraseLogRangeTaskFunc::version = 1; REGISTER_TASKFUNC(EraseLogRangeTaskFunc); @@ -1969,7 +2034,7 @@ namespace fileBackup { // Do not erase at the first time if (prevBeginVersion > 0) { state Key destUidValue = wait(config.destUidValue().getOrThrow(tr)); - Key _ = wait(EraseLogRangeTaskFunc::addTask(tr, taskBucket, config.getUid(), TaskCompletionKey::joinWith(logDispatchBatchFuture), destUidValue, prevBeginVersion, beginVersion)); + Key _ = wait(EraseLogRangeTaskFunc::addTask(tr, taskBucket, config.getUid(), TaskCompletionKey::joinWith(logDispatchBatchFuture), destUidValue, beginVersion)); } Void _ = wait(taskBucket->finish(tr, task)); @@ -2002,7 +2067,7 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Void(); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef BackupLogsDispatchTask::name = LiteralStringRef("file_backup_dispatch_logs"); + StringRef BackupLogsDispatchTask::name = LiteralStringRef("file_backup_dispatch_logs_5.2"); const uint32_t BackupLogsDispatchTask::version = 1; REGISTER_TASKFUNC(BackupLogsDispatchTask); @@ -2042,7 +2107,7 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return Void(); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef FileBackupFinishedTask::name = LiteralStringRef("file_backup_finished"); + StringRef FileBackupFinishedTask::name = LiteralStringRef("file_backup_finished_5.2"); const uint32_t FileBackupFinishedTask::version = 1; REGISTER_TASKFUNC(FileBackupFinishedTask); @@ -2199,7 +2264,7 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef BackupSnapshotManifest::name = LiteralStringRef("file_backup_write_snapshot_manifest"); + StringRef BackupSnapshotManifest::name = LiteralStringRef("file_backup_write_snapshot_manifest_5.2"); const uint32_t BackupSnapshotManifest::version = 1; REGISTER_TASKFUNC(BackupSnapshotManifest); @@ -2285,7 +2350,7 @@ namespace fileBackup { Future execute(Database cx, Reference tb, Reference fb, Reference task) { return _execute(cx, tb, fb, task); }; Future finish(Reference tr, Reference tb, Reference fb, Reference task) { return _finish(tr, tb, fb, task); }; }; - StringRef StartFullBackupTaskFunc::name = LiteralStringRef("file_backup_start"); + StringRef StartFullBackupTaskFunc::name = LiteralStringRef("file_backup_start_5.2"); const uint32_t StartFullBackupTaskFunc::version = 1; REGISTER_TASKFUNC(StartFullBackupTaskFunc); @@ -2802,7 +2867,7 @@ namespace fileBackup { TraceEvent("FileRestoreDispatch") .detail("RestoreUID", restore.getUid()) .detail("BeginVersion", beginVersion) - .detail("BeginFile", Params.beginFile().get(task)) + .detail("BeginFile", printable(Params.beginFile().get(task))) .detail("BeginBlock", Params.beginBlock().get(task)) .detail("RestoreVersion", restoreVersion) .detail("ApplyLag", applyLag) @@ -2816,7 +2881,7 @@ namespace fileBackup { TraceEvent("FileRestoreDispatch") .detail("RestoreUID", restore.getUid()) .detail("BeginVersion", beginVersion) - .detail("BeginFile", Params.beginFile().get(task)) + .detail("BeginFile", printable(Params.beginFile().get(task))) .detail("BeginBlock", Params.beginBlock().get(task)) .detail("RestoreVersion", restoreVersion) .detail("ApplyLag", applyLag) @@ -2830,7 +2895,7 @@ namespace fileBackup { TraceEvent("FileRestoreDispatch") .detail("RestoreUID", restore.getUid()) .detail("BeginVersion", beginVersion) - .detail("BeginFile", Params.beginFile().get(task)) + .detail("BeginFile", printable(Params.beginFile().get(task))) .detail("BeginBlock", Params.beginBlock().get(task)) .detail("ApplyLag", applyLag) .detail("Decision", "restore_complete") @@ -2938,7 +3003,7 @@ namespace fileBackup { TraceEvent("FileRestoreDispatch") .detail("RestoreUID", restore.getUid()) .detail("BeginVersion", beginVersion) - .detail("BeginFile", Params.beginFile().get(task)) + .detail("BeginFile", printable(Params.beginFile().get(task))) .detail("BeginBlock", Params.beginBlock().get(task)) .detail("EndVersion", endVersion) .detail("ApplyLag", applyLag) @@ -2984,7 +3049,7 @@ namespace fileBackup { TraceEvent("FileRestoreDispatch") .detail("RestoreUID", restore.getUid()) .detail("BeginVersion", beginVersion) - .detail("BeginFile", Params.beginFile().get(task)) + .detail("BeginFile", printable(Params.beginFile().get(task))) .detail("BeginBlock", Params.beginBlock().get(task)) .detail("EndVersion", endVersion) .detail("ApplyLag", applyLag) @@ -3411,8 +3476,8 @@ class FileBackupAgentImpl { tr->set(destUidLookupPath, destUidValue); } } - Version initVersion = 1; - tr->set(config.getUidAsKey().withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix), BinaryWriter::toValue(initVersion, Unversioned())); + + tr->set(config.getUidAsKey().withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix), BinaryWriter::toValue(tr->getReadVersion().get(), Unversioned())); config.destUidValue().set(tr, destUidValue); // Point the tag to this new uid diff --git a/fdbclient/IClientApi.h b/fdbclient/IClientApi.h index 80ad56183c3..56fb8bdf5ff 100644 --- a/fdbclient/IClientApi.h +++ b/fdbclient/IClientApi.h @@ -104,6 +104,8 @@ class IClientApi { virtual void stopNetwork() = 0; virtual ThreadFuture> createCluster(const char *clusterFilePath) = 0; + + virtual void addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter) = 0; }; #endif diff --git a/fdbclient/Knobs.cpp b/fdbclient/Knobs.cpp index 3f8c2595b59..9fd318e09c3 100644 --- a/fdbclient/Knobs.cpp +++ b/fdbclient/Knobs.cpp @@ -113,7 +113,7 @@ ClientKnobs::ClientKnobs(bool randomize) { init( BACKUP_MAP_KEY_UPPER_LIMIT, 1e5 ); if( buggifyMapLimits ) BACKUP_MAP_KEY_UPPER_LIMIT = 30; init( BACKUP_COPY_TASKS, 90 ); init( BACKUP_BLOCK_SIZE, LOG_RANGE_BLOCK_SIZE/10 ); - init( BACKUP_TASKS_PER_AGENT, 20 ); + init( BACKUP_TASKS_PER_AGENT, 10 ); init( SIM_BACKUP_TASKS_PER_AGENT, 10 ); init( BACKUP_RANGEFILE_BLOCK_SIZE, 1024 * 1024); init( BACKUP_LOGFILE_BLOCK_SIZE, 1024 * 1024); @@ -164,6 +164,11 @@ ClientKnobs::ClientKnobs(bool randomize) { init( BLOBSTORE_MAX_SEND_BYTES_PER_SECOND, 1e9 ); init( BLOBSTORE_MAX_RECV_BYTES_PER_SECOND, 1e9 ); + init( BLOBSTORE_LIST_REQUESTS_PER_SECOND, 25 ); + init( BLOBSTORE_WRITE_REQUESTS_PER_SECOND, 50 ); + init( BLOBSTORE_READ_REQUESTS_PER_SECOND, 100 ); + init( BLOBSTORE_DELETE_REQUESTS_PER_SECOND, 200 ); + // Client Status Info init(CSI_SAMPLING_PROBABILITY, -1.0); init(CSI_SIZE_LIMIT, std::numeric_limits::max()); diff --git a/fdbclient/Knobs.h b/fdbclient/Knobs.h index 3b69170ae2d..2e24e197bbe 100644 --- a/fdbclient/Knobs.h +++ b/fdbclient/Knobs.h @@ -151,6 +151,10 @@ class ClientKnobs : public Knobs { int BLOBSTORE_REQUEST_TRIES; int BLOBSTORE_REQUEST_TIMEOUT; int BLOBSTORE_REQUESTS_PER_SECOND; + int BLOBSTORE_LIST_REQUESTS_PER_SECOND; + int BLOBSTORE_WRITE_REQUESTS_PER_SECOND; + int BLOBSTORE_READ_REQUESTS_PER_SECOND; + int BLOBSTORE_DELETE_REQUESTS_PER_SECOND; int BLOBSTORE_CONCURRENT_REQUESTS; int BLOBSTORE_MULTIPART_MAX_PART_SIZE; int BLOBSTORE_MULTIPART_MIN_PART_SIZE; diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 75347245004..faba72a6973 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -1155,7 +1155,7 @@ ACTOR Future lockDatabase( Transaction* tr, UID id ) { } } - tr->atomicOp(databaseLockedKey, BinaryWriter::toValue(id, Unversioned()).withPrefix(LiteralStringRef("0123456789")), MutationRef::SetVersionstampedValue); + tr->atomicOp(databaseLockedKey, BinaryWriter::toValue(id, Unversioned()).withPrefix(LiteralStringRef("0123456789")).withSuffix(LiteralStringRef("\x00\x00\x00\x00")), MutationRef::SetVersionstampedValue); tr->addWriteConflictRange(normalKeys); return Void(); } @@ -1174,7 +1174,7 @@ ACTOR Future lockDatabase( Reference tr, UID id } } - tr->atomicOp(databaseLockedKey, BinaryWriter::toValue(id, Unversioned()).withPrefix(LiteralStringRef("0123456789")), MutationRef::SetVersionstampedValue); + tr->atomicOp(databaseLockedKey, BinaryWriter::toValue(id, Unversioned()).withPrefix(LiteralStringRef("0123456789")).withSuffix(LiteralStringRef("\x00\x00\x00\x00")), MutationRef::SetVersionstampedValue); tr->addWriteConflictRange(normalKeys); return Void(); } diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index bfd36fbd7e7..b98705a36fe 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -56,7 +56,7 @@ struct MasterProxyInterface { getConsistentReadVersion.getEndpoint(TaskProxyGetConsistentReadVersion); getRawCommittedVersion.getEndpoint(TaskProxyGetRawCommittedVersion); commit.getEndpoint(TaskProxyCommit); - getKeyServersLocations.getEndpoint(TaskProxyGetKeyServersLocations); + //getKeyServersLocations.getEndpoint(TaskProxyGetKeyServersLocations); //do not increase the priority of these requests, because clients cans bring down the cluster with too many of these messages. } }; diff --git a/fdbclient/MultiVersionAssignmentVars.h b/fdbclient/MultiVersionAssignmentVars.h index 750ce1d896d..b4c84f11b9e 100644 --- a/fdbclient/MultiVersionAssignmentVars.h +++ b/fdbclient/MultiVersionAssignmentVars.h @@ -135,7 +135,7 @@ class DLThreadSingleAssignmentVar : public ThreadSingleAssignmentVar { ~DLThreadSingleAssignmentVar() { lock.assertNotEntered(); if(f) { - ASSERT(futureRefCount == 1); + ASSERT_ABORT(futureRefCount == 1); api->futureDestroy(f); } } diff --git a/fdbclient/MultiVersionTransaction.actor.cpp b/fdbclient/MultiVersionTransaction.actor.cpp index 411190857b9..235f6aa8a1a 100644 --- a/fdbclient/MultiVersionTransaction.actor.cpp +++ b/fdbclient/MultiVersionTransaction.actor.cpp @@ -336,7 +336,21 @@ void DLApi::setupNetwork() { } void DLApi::runNetwork() { - throwIfError(api->runNetwork()); + auto e = api->runNetwork(); + + for(auto &hook : threadCompletionHooks) { + try { + hook.first(hook.second); + } + catch(Error &e) { + TraceEvent(SevError, "NetworkShutdownHookError").error(e); + } + catch(...) { + TraceEvent(SevError, "NetworkShutdownHookError").error(unknown_error()); + } + } + + throwIfError(e); } void DLApi::stopNetwork() { @@ -355,6 +369,11 @@ ThreadFuture> DLApi::createCluster(const char *clusterFilePa }); } +void DLApi::addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter) { + MutexHolder holder(lock); + threadCompletionHooks.push_back(std::make_pair(hook, hookParameter)); +} + // MultiVersionTransaction MultiVersionTransaction::MultiVersionTransaction(Reference db) : db(db) { updateTransaction(); @@ -1140,19 +1159,6 @@ THREAD_FUNC_RETURN runNetworkThread(void *param) { TraceEvent(SevError, "RunNetworkError").error(e); } - std::vector> &hooks = ((ClientInfo*)param)->threadCompletionHooks; - for(auto &hook : hooks) { - try { - hook.first(hook.second); - } - catch(Error &e) { - TraceEvent(SevError, "NetworkShutdownHookError").error(e); - } - catch(...) { - TraceEvent(SevError, "NetworkShutdownHookError").error(unknown_error()); - } - } - THREAD_RETURN; } @@ -1174,29 +1180,7 @@ void MultiVersionApi::runNetwork() { }); } - Error *runErr = NULL; - try { - localClient->api->runNetwork(); - } - catch(Error &e) { - runErr = &e; - } - - for(auto &hook : localClient->threadCompletionHooks) { - try { - hook.first(hook.second); - } - catch(Error &e) { - TraceEvent(SevError, "NetworkShutdownHookError").error(e); - } - catch(...) { - TraceEvent(SevError, "NetworkShutdownHookError").error(unknown_error()); - } - } - - if(runErr != NULL) { - throw *runErr; - } + localClient->api->runNetwork(); for(auto h : handles) { waitThread(h); @@ -1220,7 +1204,7 @@ void MultiVersionApi::stopNetwork() { } } -void MultiVersionApi::addNetworkThreadCompletionHook(void (*hook)(void*), void *hook_parameter) { +void MultiVersionApi::addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter) { lock.enter(); if(!networkSetup) { lock.leave(); @@ -1228,13 +1212,12 @@ void MultiVersionApi::addNetworkThreadCompletionHook(void (*hook)(void*), void * } lock.leave(); - auto hookPair = std::pair(hook, hook_parameter); - threadCompletionHooks.push_back(hookPair); + localClient->api->addNetworkThreadCompletionHook(hook, hookParameter); if(!bypassMultiClientApi) { - for( auto it : externalClients ) { - it.second->threadCompletionHooks.push_back(hookPair); - } + runOnExternalClients([hook, hookParameter](Reference client) { + client->api->addNetworkThreadCompletionHook(hook, hookParameter); + }); } } diff --git a/fdbclient/MultiVersionTransaction.h b/fdbclient/MultiVersionTransaction.h index c40ea013a3f..f52caf35764 100644 --- a/fdbclient/MultiVersionTransaction.h +++ b/fdbclient/MultiVersionTransaction.h @@ -203,12 +203,17 @@ class DLApi : public IClientApi { ThreadFuture> createCluster(const char *clusterFilePath); + void addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter); + private: const std::string fdbCPath; const Reference api; int headerVersion; bool networkSetup; + Mutex lock; + std::vector> threadCompletionHooks; + void init(); }; @@ -403,7 +408,7 @@ class MultiVersionApi : public IClientApi { void setupNetwork(); void runNetwork(); void stopNetwork(); - void addNetworkThreadCompletionHook(void (*hook)(void*), void *hook_parameter); + void addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter); ThreadFuture> createCluster(const char *clusterFilePath); static MultiVersionApi* api; @@ -443,7 +448,6 @@ class MultiVersionApi : public IClientApi { std::vector>>> options; std::map>> setEnvOptions; volatile bool envOptionsLoaded; - std::vector> threadCompletionHooks; }; diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index d5aba169d1e..a0de3b9a482 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -93,7 +93,7 @@ LocationInfo::~LocationInfo() { for( auto const& alternative : getAlternatives() ) handles.push_back( alternative.v.getVersion.getEndpoint().token ); // must match above choice of UID std::sort( handles.begin(), handles.end() ); - ASSERT( handles.size() ); + ASSERT_ABORT( handles.size() ); auto it = cx->ssid_locationInfo.find( handles ); if( it != cx->ssid_locationInfo.end() ) @@ -124,6 +124,10 @@ std::string printable( const StringRef& val ) { return val.printable(); } +std::string printable( const std::string& str ) { + return StringRef(str).printable(); +} + std::string printable( const Optional& val ) { if( val.present() ) return printable( val.get() ); @@ -281,7 +285,7 @@ ACTOR static Future transactionInfoCommitActor(Transaction *tr, std::vecto int64_t numCommitBytes = 0; for (auto &chunk : *chunks) { tr->atomicOp(chunk.key, chunk.value, MutationRef::SetVersionstampedKey); - numCommitBytes += chunk.key.size() + chunk.value.size() - 2; // subtract last 2 bytes of key that denotes verstion stamp index + numCommitBytes += chunk.key.size() + chunk.value.size() - 4; // subtract number of bytes of key that denotes verstion stamp index } tr->atomicOp(clientLatencyAtomicCtr, StringRef((uint8_t*)&numCommitBytes, 8), MutationRef::AddValue); Void _ = wait(tr->commit()); @@ -365,9 +369,9 @@ ACTOR static Future clientStatusUpdateActor(DatabaseContext *cx) { TrInfoChunk chunk; BinaryWriter chunkBW(Unversioned()); chunkBW << bigEndian32(i+1) << bigEndian32(num_chunks); - chunk.key = KeyRef(clientLatencyName + std::string(10, '\x00') + "/" + random_id + "/" + chunkBW.toStringRef().toString() + "/" + std::string(2, '\x00')); - int16_t pos = littleEndian16(clientLatencyName.size()); - memcpy(mutateString(chunk.key) + chunk.key.size() - sizeof(int16_t), &pos, sizeof(int16_t)); + chunk.key = KeyRef(clientLatencyName + std::string(10, '\x00') + "/" + random_id + "/" + chunkBW.toStringRef().toString() + "/" + std::string(4, '\x00')); + int32_t pos = littleEndian32(clientLatencyName.size()); + memcpy(mutateString(chunk.key) + chunk.key.size() - sizeof(int32_t), &pos, sizeof(int32_t)); if (i == num_chunks - 1) { chunk.value = ValueRef(static_cast(bw.getData()) + (i * value_size_limit), bw.getLength() - (i * value_size_limit)); } @@ -545,7 +549,7 @@ DatabaseContext::~DatabaseContext() { monitorMasterProxiesInfoChange.cancel(); for(auto it = ssid_locationInfo.begin(); it != ssid_locationInfo.end(); it = ssid_locationInfo.erase(it)) it->second->notifyContextDestroyed(); - ASSERT( ssid_locationInfo.empty() ); + ASSERT_ABORT( ssid_locationInfo.empty() ); locationCache.insert( allKeys, Reference() ); } @@ -786,6 +790,18 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu case FDBNetworkOptions::TLS_CERT_BYTES: tlsOptions->set_cert_data( value.get().toString() ); break; + case FDBNetworkOptions::TLS_CA_PATH: + validateOptionValue(value, true); + tlsOptions->set_ca_file( value.get().toString() ); + break; + case FDBNetworkOptions::TLS_CA_BYTES: + validateOptionValue(value, true); + tlsOptions->set_ca_data(value.get().toString()); + break; + case FDBNetworkOptions::TLS_PASSWORD: + validateOptionValue(value, true); + tlsOptions->set_key_password(value.get().toString()); + break; case FDBNetworkOptions::TLS_KEY_PATH: validateOptionValue(value, true); tlsOptions->set_key_file( value.get().toString() ); @@ -797,7 +813,7 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu case FDBNetworkOptions::TLS_VERIFY_PEERS: validateOptionValue(value, true); try { - tlsOptions->set_verify_peers( value.get().toString() ); + tlsOptions->set_verify_peers({ value.get().toString() }); } catch( Error& e ) { TraceEvent(SevWarnAlways, "TLSValidationSetError") .detail("Input", value.get().toString() ) @@ -1007,17 +1023,13 @@ ACTOR Future>> transactionalGetServerInt } //If isBackward == true, returns the shard containing the key before 'key' (an infinitely long, inexpressible key). Otherwise returns the shard containing key -ACTOR Future< pair> > getKeyLocation( Database cx, Key key, TransactionInfo info, bool isBackward = false ) { +ACTOR Future< pair> > getKeyLocation_internal( Database cx, Key key, TransactionInfo info, bool isBackward = false ) { if (isBackward) { ASSERT( key != allKeys.begin && key <= allKeys.end ); } else { ASSERT( key < allKeys.end ); } - auto loc = cx->getCachedLocation( key, isBackward ); - if (loc.second) - return loc; - if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocation.Before"); @@ -1036,6 +1048,24 @@ ACTOR Future< pair> > getKeyLocation( Database } } +template +Future>> getKeyLocation( Database const& cx, Key const& key, F StorageServerInterface::*member, TransactionInfo const& info, bool isBackward = false ) { + auto ssi = cx->getCachedLocation( key, isBackward ); + if (!ssi.second) { + return getKeyLocation_internal( cx, key, info, isBackward ); + } + + for(int i = 0; i < ssi.second->size(); i++) { + if( IFailureMonitor::failureMonitor().onlyEndpointFailed(ssi.second->get(i, member).getEndpoint()) ) { + cx->invalidateCache( key ); + ssi.second.clear(); + return getKeyLocation_internal( cx, key, info, isBackward ); + } + } + + return ssi; +} + ACTOR Future< vector< pair> > > getKeyRangeLocations_internal( Database cx, KeyRange keys, int limit, bool reverse, TransactionInfo info ) { if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocations.Before"); @@ -1063,14 +1093,36 @@ ACTOR Future< vector< pair> > > getKeyRangeLoca } } -Future< vector< pair> > > getKeyRangeLocations( Database cx, KeyRange keys, int limit, bool reverse, TransactionInfo info ) { +template +Future< vector< pair> > > getKeyRangeLocations( Database const& cx, KeyRange const& keys, int limit, bool reverse, F StorageServerInterface::*member, TransactionInfo const& info ) { ASSERT (!keys.empty()); - vector< pair> > result; - if (cx->getCachedLocations(keys, result, limit, reverse)) - return result; + vector< pair> > locations; + if (!cx->getCachedLocations(keys, locations, limit, reverse)) { + return getKeyRangeLocations_internal( cx, keys, limit, reverse, info ); + } + + bool foundFailed = false; + for(auto& it : locations) { + bool onlyEndpointFailed = false; + for(int i = 0; i < it.second->size(); i++) { + if( IFailureMonitor::failureMonitor().onlyEndpointFailed(it.second->get(i, member).getEndpoint()) ) { + onlyEndpointFailed = true; + break; + } + } + + if( onlyEndpointFailed ) { + cx->invalidateCache( it.first.begin ); + foundFailed = true; + } + } + + if(foundFailed) { + return getKeyRangeLocations_internal( cx, keys, limit, reverse, info ); + } - return getKeyRangeLocations_internal( cx, keys, limit, reverse, info ); + return locations; } ACTOR Future warmRange_impl( Transaction *self, Database cx, KeyRange keys ) { @@ -1113,28 +1165,8 @@ ACTOR Future> getValue( Future version, Key key, Databa state Version ver = wait( version ); validateVersion(ver); - state pair> ssi; loop { - ssi = cx->getCachedLocation( key ); - if (!ssi.second) { - pair> ssi2 = wait( getKeyLocation( cx, key, info ) ); - ssi = std::move(ssi2); - } else { - bool onlyEndpointFailed = false; - for(int i = 0; i < ssi.second->size(); i++) { - if( IFailureMonitor::failureMonitor().onlyEndpointFailed(ssi.second->get(i, &StorageServerInterface::getValue).getEndpoint()) ) { - onlyEndpointFailed = true; - break; - } - } - - if( onlyEndpointFailed ) { - cx->invalidateCache( key ); - pair> ssi2 = wait( getKeyLocation( cx, key, info ) ); - ssi = std::move(ssi2); - } - } - //Reference ssi = wait( getKeyLocation( cx, key ) ); + state pair> ssi = wait( getKeyLocation(cx, key, &StorageServerInterface::getValue, info) ); state Optional getValueID = Optional(); state uint64_t startTime; state double startTimeD; @@ -1212,7 +1244,9 @@ ACTOR Future getKey( Database cx, KeySelector k, Future version, T return Key(); } - state pair> ssi = wait( getKeyLocation(cx, Key(k.getKey(), k.arena()), info, k.isBackward()) ); + Key locationKey(k.getKey(), k.arena()); + state pair> ssi = wait( getKeyLocation(cx, locationKey, &StorageServerInterface::getKey, info, k.isBackward()) ); + try { if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKey.Before"); //.detail("StartKey", printable(k.getKey())).detail("offset",k.offset).detail("orEqual",k.orEqual); @@ -1270,11 +1304,7 @@ ACTOR Future< Void > watchValue( Future version, Key key, Optional> ssi = cx->getCachedLocation( key ); - if (!ssi.second) { - pair> ssi2 = wait( getKeyLocation( cx, key, info ) ); - ssi = std::move(ssi2); - } + state pair> ssi = wait( getKeyLocation(cx, key, &StorageServerInterface::watchValue, info ) ); try { state Optional watchValueID = Optional(); @@ -1345,8 +1375,7 @@ ACTOR Future> getExactRange( Database cx, Version ver //printf("getExactRange( '%s', '%s' )\n", keys.begin.toString().c_str(), keys.end.toString().c_str()); loop { - state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, CLIENT_KNOBS->GET_RANGE_SHARD_LIMIT, reverse, info ) ); - + state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, CLIENT_KNOBS->GET_RANGE_SHARD_LIMIT, reverse, &StorageServerInterface::getKeyValues, info ) ); ASSERT( locations.size() ); state int shard = 0; loop { @@ -1601,7 +1630,9 @@ ACTOR Future> getRange( Database cx, Reference> beginServer = wait( getKeyLocation( cx, reverse ? Key(end.getKey(), end.arena()) : Key(begin.getKey(), begin.arena()), info, reverse ? (end-1).isBackward() : begin.isBackward() ) ); + Key locationKey = reverse ? Key(end.getKey(), end.arena()) : Key(begin.getKey(), begin.arena()); + bool locationBackward = reverse ? (end-1).isBackward() : begin.isBackward(); + state pair> beginServer = wait( getKeyLocation( cx, locationKey, &StorageServerInterface::getKeyValues, info, locationBackward ) ); state KeyRange shard = beginServer.first; state bool modifiedSelectors = false; state GetKeyValuesRequest req; @@ -2056,10 +2087,12 @@ void Transaction::atomicOp(const KeyRef& key, const ValueRef& operand, MutationR else if (operationType == MutationRef::And) operationType = MutationRef::AndV2; } + auto &req = tr; auto &t = req.transaction; auto r = singleKeyRange( key, req.arena ); auto v = ValueRef( req.arena, operand ); + t.mutations.push_back( req.arena, MutationRef( operationType, r.begin, v ) ); if( addConflictRange ) @@ -2423,11 +2456,6 @@ ACTOR static Future tryCommit( Database cx, Reference } Future Transaction::commitMutations() { - cx->transactionsCommitStarted++; - - if(options.readOnly) - return transaction_read_only(); - try { //if this is a read-only transaction return immediately if( !tr.transaction.write_conflict_ranges.size() && !tr.transaction.mutations.size() ) { @@ -2438,6 +2466,11 @@ Future Transaction::commitMutations() { return Void(); } + cx->transactionsCommitStarted++; + + if(options.readOnly) + return transaction_read_only(); + cx->mutationsPerCommit.addSample(tr.transaction.mutations.size()); cx->bytesPerCommit.addSample(tr.transaction.mutations.expectedSize()); @@ -2854,8 +2887,8 @@ ACTOR Future< StorageMetrics > waitStorageMetrics( { state int tooManyShardsCount = 0; loop { - state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, shardLimit, false, TransactionInfo(TaskDataDistribution) ) ); - + state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, shardLimit, false, &StorageServerInterface::waitMetrics, TransactionInfo(TaskDataDistribution) ) ); + if( locations.size() == shardLimit ) { TraceEvent(!g_network->isSimulated() && ++tooManyShardsCount >= 15 ? SevWarnAlways : SevWarn, "WaitStorageMetricsPenalty") .detail("Keys", printable(keys)) @@ -2908,7 +2941,7 @@ Future< StorageMetrics > Transaction::getStorageMetrics( KeyRange const& keys, i ACTOR Future< Standalone> > splitStorageMetrics( Database cx, KeyRange keys, StorageMetrics limit, StorageMetrics estimated ) { loop { - state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT, false, TransactionInfo(TaskDataDistribution) ) ); + state vector< pair> > locations = wait( getKeyRangeLocations( cx, keys, CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT, false, &StorageServerInterface::splitMetrics, TransactionInfo(TaskDataDistribution) ) ); state StorageMetrics used; state Standalone> results; diff --git a/fdbclient/NativeAPI.h b/fdbclient/NativeAPI.h index b87d537b043..e4f77dabff8 100644 --- a/fdbclient/NativeAPI.h +++ b/fdbclient/NativeAPI.h @@ -129,6 +129,7 @@ class Cluster : public ReferenceCounted, NonCopyable { private: friend class ThreadSafeCluster; friend class AtomicOpsApiCorrectnessWorkload; // This is just for testing purposes. It needs to change apiVersion friend class AtomicOpsWorkload; // This is just for testing purposes. It needs to change apiVersion + friend class VersionStampWorkload; // This is just for testing purposes. It needs to change apiVersion Cluster( Reference connFile, int apiVersion = API_VERSION_LATEST ); diff --git a/fdbclient/RYWIterator.cpp b/fdbclient/RYWIterator.cpp index b42cbdcea87..9b8085608d7 100644 --- a/fdbclient/RYWIterator.cpp +++ b/fdbclient/RYWIterator.cpp @@ -386,7 +386,7 @@ TEST_CASE("fdbclient/WriteMap/setVersionstampedKey") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("stamp:XXXXXXXX\x06\x00"), MutationRef::SetVersionstampedKey, LiteralStringRef("1"), true); + writes.mutate(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00"), MutationRef::SetVersionstampedKey, LiteralStringRef("1"), true); ASSERT(!writes.empty()); ASSERT(getWriteMapCount(&writes) == 3); @@ -398,7 +398,7 @@ TEST_CASE("fdbclient/WriteMap/setVersionstampedKey") { ASSERT(it.beginKey() < allKeys.end); ASSERT(it.beginKey().cmp(LiteralStringRef("")) == 0); - ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00")) == 0); + ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -407,8 +407,8 @@ TEST_CASE("fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00")) == 0); - ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00")) == 0); + ASSERT(it.beginKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 0); + ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 0); ASSERT(!it.is_cleared_range()); ASSERT(it.is_conflict_range()); ASSERT(it.is_operation()); @@ -418,7 +418,7 @@ TEST_CASE("fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00")) == 0); + ASSERT(it.beginKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 0); ASSERT(it.endKey().cmp(LiteralStringRef("stamp:ZZZZZZZZZZ")) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); @@ -459,7 +459,7 @@ TEST_CASE("fdbclient/WriteMap/setVersionstampedValue") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("stamp"), MutationRef::SetVersionstampedValue, LiteralStringRef("XXXXXXXX\x00\x00"), true); + writes.mutate(LiteralStringRef("stamp"), MutationRef::SetVersionstampedValue, LiteralStringRef("XXXXXXXX\x00\x00\x00\x00\x00\x00"), true); ASSERT(!writes.empty()); ASSERT(getWriteMapCount(&writes) == 3); diff --git a/fdbclient/RYWIterator.h b/fdbclient/RYWIterator.h index 9d3d1fc5156..0fe0cdd3d20 100644 --- a/fdbclient/RYWIterator.h +++ b/fdbclient/RYWIterator.h @@ -82,6 +82,12 @@ class RandomTestImpl { static ValueRef getRandomVersionstampValue(Arena& arena) { int len = g_random->randomInt(10, 98); std::string value = std::string(len, 'x'); + int32_t pos = g_random->randomInt(0, len - 9); + if (g_random->random01() < 0.01) { + pos = value.size() - 10; + } + pos = littleEndian32(pos); + value += std::string((const char*)&pos, sizeof(int32_t)); return ValueRef(arena, value); } @@ -92,15 +98,15 @@ class RandomTestImpl { key += '\x00'; if (idx % 3 >= 2) key += '\x00'; - int pos = key.size() - g_random->randomInt(0, 3); + int32_t pos = key.size() - g_random->randomInt(0, 3); if (g_random->random01() < 0.01) { pos = 0; } key = key.substr(0, pos); key += "XXXXXXXXYY"; key += std::string(g_random->randomInt(0, 3), 'z'); - key += (char)(pos & 0xFF); - key += (char)((pos >> 8) & 0xFF); + pos = littleEndian32(pos); + key += std::string((const char*)&pos, sizeof(int32_t)); return ValueRef(arena, key); } @@ -132,4 +138,4 @@ class RandomTestImpl { void testESR(); void testSnapshotCache(); -#endif \ No newline at end of file +#endif diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 5452215d941..313a725fa09 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -940,8 +940,8 @@ class RYWImpl { std::swap(itCopy->value[i--], itCopy->value.back()); itCopy->value.pop_back(); } else if( !valueKnown || - (itCopy->value[i]->setPresent && (itCopy->value[i]->setValue.present() != val.present() || (val.present() && itCopy->value[i]->setValue.get() != val.get()))) || - (itCopy->value[i]->valuePresent && (itCopy->value[i]->value.present() != val.present() || (val.present() && itCopy->value[i]->value.get() != val.get()))) ) { + (itCopy->value[i]->setPresent && (itCopy->value[i]->setValue.present() != val.present() || (val.present() && itCopy->value[i]->setValue.get() != val.get()))) || + (itCopy->value[i]->valuePresent && (itCopy->value[i]->value.present() != val.present() || (val.present() && itCopy->value[i]->value.get() != val.get()))) ) { itCopy->value[i]->onChangeTrigger.send(Void()); if( i < itCopy->value.size() - 1 ) std::swap(itCopy->value[i--], itCopy->value.back()); @@ -1487,33 +1487,50 @@ void ReadYourWritesTransaction::atomicOp( const KeyRef& key, const ValueRef& ope if(operand.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT) throw value_too_large(); + if (tr.apiVersionAtLeast(510)) { + if (operationType == MutationRef::Min) + operationType = MutationRef::MinV2; + else if (operationType == MutationRef::And) + operationType = MutationRef::AndV2; + } + + KeyRef k; + if(!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedKey) { + k = key.withSuffix( LiteralStringRef("\x00\x00"), arena ); + } else { + k = KeyRef( arena, key ); + } + ValueRef v; + if(!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedValue) { + v = operand.withSuffix( LiteralStringRef("\x00\x00\x00\x00"), arena ); + } else { + v = ValueRef( arena, operand ); + } + if(operationType == MutationRef::SetVersionstampedKey) { - KeyRangeRef range = getVersionstampKeyRange(arena, key, getMaxReadKey()); // this does validation of the key and needs to be performed before the readYourWritesDisabled path + KeyRangeRef range = getVersionstampKeyRange(arena, k, getMaxReadKey()); // this does validation of the key and needs to be performed before the readYourWritesDisabled path if(!options.readYourWritesDisabled) { writeRangeToNativeTransaction(range); writes.addUnmodifiedAndUnreadableRange(range); } } - if (operationType == MutationRef::SetVersionstampedValue && operand.size() < 10) - throw client_invalid_operation(); - - if (tr.apiVersionAtLeast(510)) { - if (operationType == MutationRef::Min) - operationType = MutationRef::MinV2; - else if (operationType == MutationRef::And) - operationType = MutationRef::AndV2; + if(operationType == MutationRef::SetVersionstampedValue) { + if(v.size() < 4) + throw client_invalid_operation(); + int32_t pos; + memcpy(&pos, v.end() - sizeof(int32_t), sizeof(int32_t)); + pos = littleEndian32(pos); + if (pos < 0 || pos + 10 > v.size() - 4) + throw client_invalid_operation(); } if(options.readYourWritesDisabled) { - return tr.atomicOp(key, operand, (MutationRef::Type) operationType, addWriteConflict); + return tr.atomicOp(k, v, (MutationRef::Type) operationType, addWriteConflict); } - KeyRef k = KeyRef( arena, key ); - ValueRef v = ValueRef( arena, operand ); - writes.mutate(k, (MutationRef::Type) operationType, v, addWriteConflict); - RYWImpl::triggerWatches(this, key, Optional(), false); + RYWImpl::triggerWatches(this, k, Optional(), false); } void ReadYourWritesTransaction::set( const KeyRef& key, const ValueRef& value ) { diff --git a/fdbclient/StatusClient.actor.cpp b/fdbclient/StatusClient.actor.cpp index 9976ef87f0e..44c8766ce38 100644 --- a/fdbclient/StatusClient.actor.cpp +++ b/fdbclient/StatusClient.actor.cpp @@ -325,7 +325,7 @@ ACTOR Future clientStatusFetcher(Reference description += ClusterConnectionFile(f->getFilename()).getConnectionString().toString().c_str(); description += "\nThe current connection string is: "; description += f->getConnectionString().toString().c_str(); - description += "\nVerify cluster file is writable and has not been overwritten externally. To change coordinators without manual intervention, the cluster file and its containing folder must be writable by all servers and clients. If a majority of the coordinators referenced by the old connection string are lost, the database will stop working until the correct cluster file is distributed to all processes."; + description += "\nVerify the cluster file and its parent directory are writable and that the cluster file has not been overwritten externally. To change coordinators without manual intervention, the cluster file and its containing folder must be writable by all servers and clients. If a majority of the coordinators referenced by the old connection string are lost, the database will stop working until the correct cluster file is distributed to all processes."; messages->push_back(makeMessage("incorrect_cluster_file_contents", description.c_str())); } diff --git a/fdbclient/StorageServerInterface.h b/fdbclient/StorageServerInterface.h index 1f5ca44c10e..8c59ec3f1f8 100644 --- a/fdbclient/StorageServerInterface.h +++ b/fdbclient/StorageServerInterface.h @@ -343,12 +343,11 @@ struct StorageQueuingMetricsReply { int64_t instanceID; // changes if bytesDurable and bytesInput reset int64_t bytesDurable, bytesInput; StorageBytes storageBytes; - double readReplyRate; // for status Version v; // current storage server version template void serialize(Ar& ar) { - ar & localTime & instanceID & bytesDurable & bytesInput & readReplyRate & v & storageBytes; + ar & localTime & instanceID & bytesDurable & bytesInput & v & storageBytes; } }; diff --git a/fdbclient/ThreadSafeTransaction.actor.cpp b/fdbclient/ThreadSafeTransaction.actor.cpp index 5b19433abd6..9f64ade2227 100644 --- a/fdbclient/ThreadSafeTransaction.actor.cpp +++ b/fdbclient/ThreadSafeTransaction.actor.cpp @@ -357,7 +357,30 @@ void ThreadSafeApi::setupNetwork() { } void ThreadSafeApi::runNetwork() { - ::runNetwork(); + Optional runErr; + try { + ::runNetwork(); + } + catch(Error &e) { + runErr = e; + } + + for(auto &hook : threadCompletionHooks) { + try { + hook.first(hook.second); + } + catch(Error &e) { + TraceEvent(SevError, "NetworkShutdownHookError").error(e); + } + catch(...) { + TraceEvent(SevError, "NetworkShutdownHookError").error(unknown_error()); + } + } + + if(runErr.present()) { + throw runErr.get(); + } + } void ThreadSafeApi::stopNetwork() { @@ -368,4 +391,14 @@ ThreadFuture> ThreadSafeApi::createCluster(const char *clust return ThreadSafeCluster::create(clusterFilePath, apiVersion); } +void ThreadSafeApi::addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter) { + if (!g_network) { + throw network_not_setup(); + } + + MutexHolder holder(lock); // We could use the network thread to protect this action, but then we can't guarantee upon return that the hook is set. + threadCompletionHooks.push_back(std::make_pair(hook, hookParameter)); +} + + IClientApi* ThreadSafeApi::api = new ThreadSafeApi(); diff --git a/fdbclient/ThreadSafeTransaction.h b/fdbclient/ThreadSafeTransaction.h index 54732c03e84..272fa2332eb 100644 --- a/fdbclient/ThreadSafeTransaction.h +++ b/fdbclient/ThreadSafeTransaction.h @@ -140,6 +140,8 @@ class ThreadSafeApi : public IClientApi, ThreadSafeReferenceCounted> createCluster(const char *clusterFilePath); + void addNetworkThreadCompletionHook(void (*hook)(void*), void *hookParameter); + static IClientApi* api; private: @@ -148,6 +150,9 @@ class ThreadSafeApi : public IClientApi, ThreadSafeReferenceCounted> threadCompletionHooks; }; #endif diff --git a/fdbclient/WriteMap.h b/fdbclient/WriteMap.h index 444ed750eb9..d02ce009e07 100644 --- a/fdbclient/WriteMap.h +++ b/fdbclient/WriteMap.h @@ -142,7 +142,7 @@ class WriteMap { bool following_conflict = it.entry().following_keys_conflict; bool is_conflict = addConflict || it.is_conflict_range(); bool following_unreadable = it.entry().following_keys_unreadable; - bool is_unreadable = it.is_unreadable() || operation == MutationRef::SetVersionstampedValue || operation == MutationRef::SetVersionstampedKey; + bool is_unreadable = it.is_unreadable() || operation == MutationRef::SetVersionstampedValue || operation == MutationRef::SetVersionstampedKey; bool is_dependent = operation != MutationRef::SetValue && operation != MutationRef::SetVersionstampedValue && operation != MutationRef::SetVersionstampedKey; if (it.entry().key != key) { @@ -630,4 +630,4 @@ class WriteMap { */ -#endif \ No newline at end of file +#endif diff --git a/fdbclient/vexillographer/fdb.options b/fdbclient/vexillographer/fdb.options index 5a6e91001f9..467e0a1adcb 100644 --- a/fdbclient/vexillographer/fdb.options +++ b/fdbclient/vexillographer/fdb.options @@ -79,6 +79,15 @@ description is not currently required but encouraged.