chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,451 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <brpc/couchbase.h>
|
||||
#include <butil/logging.h>
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// ANSI color codes for console output
|
||||
#define GREEN "\033[32m"
|
||||
#define RED "\033[31m"
|
||||
#define RESET "\033[0m"
|
||||
|
||||
DEFINE_string(server, "localhost:11210", "IP Address of server");
|
||||
int performOperations(brpc::CouchbaseOperations& couchbase_ops) {
|
||||
std::string add_key = "user::test_brpc_binprot";
|
||||
std::string add_value =
|
||||
R"({"name": "John Doe", "age": 30, "email": "john@example.com"})";
|
||||
|
||||
brpc::CouchbaseOperations::Result add_result =
|
||||
couchbase_ops.add(add_key, add_value);
|
||||
if (add_result.success) {
|
||||
std::cout << GREEN << "ADD operation successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "ADD operation failed: " << add_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// Try to ADD the same key again (should fail with key exists)
|
||||
brpc::CouchbaseOperations::Result add_result2 =
|
||||
couchbase_ops.add(add_key, add_value);
|
||||
if (add_result2.success) {
|
||||
std::cout << GREEN << "Second ADD operation unexpectedly successful"
|
||||
<< RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "Second ADD operation failed as expected: "
|
||||
<< add_result2.error_message << RESET << std::endl;
|
||||
}
|
||||
// Get operation using high-level method
|
||||
brpc::CouchbaseOperations::Result get_result = couchbase_ops.get(add_key);
|
||||
if (get_result.success) {
|
||||
std::cout << GREEN << "GET operation successful" << RESET << std::endl;
|
||||
std::cout << "GET value: " << get_result.value << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "GET operation failed: " << get_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// Add binprot item1 using high-level method
|
||||
std::string item1_key = "binprot_item1";
|
||||
brpc::CouchbaseOperations::Result item1_result =
|
||||
couchbase_ops.add(item1_key, add_value);
|
||||
if (item1_result.success) {
|
||||
std::cout << GREEN << "ADD binprot item1 successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "ADD binprot item1 failed: " << item1_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// Add binprot item2 using high-level method
|
||||
std::string item2_key = "binprot_item2";
|
||||
brpc::CouchbaseOperations::Result item2_result =
|
||||
couchbase_ops.add(item2_key, add_value);
|
||||
if (item2_result.success) {
|
||||
std::cout << GREEN << "ADD binprot item2 successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "ADD binprot item2 failed: " << item2_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// Add binprot item3 using high-level method
|
||||
std::string item3_key = "binprot_item3";
|
||||
brpc::CouchbaseOperations::Result item3_result =
|
||||
couchbase_ops.add(item3_key, add_value);
|
||||
if (item3_result.success) {
|
||||
std::cout << GREEN << "ADD binprot item3 successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "ADD binprot item3 failed: " << item3_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// Perform an UPSERT on the existing key using high-level method
|
||||
std::string upsert_key = "user::test_brpc_binprot";
|
||||
std::string upsert_value =
|
||||
R"({"name": "Upserted Jane Doe", "age": 28, "email": "upserted.doe@example.com"})";
|
||||
brpc::CouchbaseOperations::Result upsert_result =
|
||||
couchbase_ops.upsert(upsert_key, upsert_value);
|
||||
if (upsert_result.success) {
|
||||
std::cout
|
||||
<< GREEN
|
||||
<< "UPSERT operation successful when the document exists in the server"
|
||||
<< RESET << std::endl;
|
||||
} else {
|
||||
std::cout
|
||||
<< RED
|
||||
<< "UPSERT operation failed when the document exists in the server: "
|
||||
<< upsert_result.error_message << RESET << std::endl;
|
||||
}
|
||||
// Do UPSERT operation on a new document using high-level method
|
||||
std::string new_upsert_key = "user::test_brpc_new_upsert";
|
||||
std::string new_upsert_value =
|
||||
R"({"name": "Jane Doe", "age": 28, "email": "jane.doe@example.com"})";
|
||||
brpc::CouchbaseOperations::Result new_upsert_result =
|
||||
couchbase_ops.upsert(new_upsert_key, new_upsert_value);
|
||||
if (new_upsert_result.success) {
|
||||
std::cout << GREEN
|
||||
<< "UPSERT operation successful when the document doesn't exist "
|
||||
"in the server"
|
||||
<< RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "UPSERT operation failed when document does not exist in the "
|
||||
"server: "
|
||||
<< new_upsert_result.error_message << RESET << std::endl;
|
||||
}
|
||||
|
||||
// Check the upserted data using high-level method
|
||||
std::string check_key = "user::test_brpc_new_upsert";
|
||||
brpc::CouchbaseOperations::Result check_result = couchbase_ops.get(check_key);
|
||||
if (check_result.success) {
|
||||
std::cout << GREEN << "GET after UPSERT operation successful - Value: "
|
||||
<< check_result.value << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "GET after UPSERT operation failed: "
|
||||
<< check_result.error_message << RESET << std::endl;
|
||||
}
|
||||
|
||||
// Delete a non-existent key using high-level method
|
||||
std::string delete_key = "Nonexistent_key";
|
||||
brpc::CouchbaseOperations::Result delete_result =
|
||||
couchbase_ops.delete_(delete_key);
|
||||
if (delete_result.success) {
|
||||
std::cout << GREEN << "DELETE operation successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "DELETE operation failed: as expected "
|
||||
<< delete_result.error_message << RESET << std::endl;
|
||||
}
|
||||
|
||||
// Delete the existing key using high-level method
|
||||
std::string delete_existing_key = "user::test_brpc_binprot";
|
||||
brpc::CouchbaseOperations::Result delete_existing_result =
|
||||
couchbase_ops.delete_(delete_existing_key);
|
||||
if (delete_existing_result.success) {
|
||||
std::cout << GREEN << "DELETE operation successful" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "DELETE operation failed: "
|
||||
<< delete_existing_result.error_message << RESET << std::endl;
|
||||
}
|
||||
|
||||
// Retrieve Collection ID for scope `_default` and collection
|
||||
// `col1`
|
||||
const std::string scope_name = "_default"; // default scope
|
||||
std::string collection_name = "col1"; // target collection
|
||||
// ------------------------------------------------------------------
|
||||
// Collection-scoped CRUD operations (only if collection id was retrieved)
|
||||
// ------------------------------------------------------------------
|
||||
// 1. ADD in collection using high-level method
|
||||
std::string coll_key = "user::collection_doc";
|
||||
std::string coll_value = R"({"type":"collection","op":"add","v":1})";
|
||||
brpc::CouchbaseOperations::Result coll_add_result =
|
||||
couchbase_ops.add(coll_key, coll_value, collection_name);
|
||||
if (coll_add_result.success) {
|
||||
std::cout << GREEN << "Collection ADD success" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "Collection ADD failed: " << coll_add_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
// 2. GET from collection using high-level method
|
||||
brpc::CouchbaseOperations::Result coll_get_result =
|
||||
couchbase_ops.get(coll_key, collection_name);
|
||||
if (coll_get_result.success) {
|
||||
std::cout << GREEN
|
||||
<< "Collection GET success value=" << coll_get_result.value
|
||||
<< RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "Collection GET failed: " << coll_get_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// 3. UPSERT in collection using high-level method
|
||||
std::string coll_upsert_value =
|
||||
R"({"type":"collection","op":"upsert","v":2})";
|
||||
brpc::CouchbaseOperations::Result coll_upsert_result =
|
||||
couchbase_ops.upsert(coll_key, coll_upsert_value, collection_name);
|
||||
if (coll_upsert_result.success) {
|
||||
std::cout << GREEN << "Collection UPSERT success" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "Collection UPSERT failed: "
|
||||
<< coll_upsert_result.error_message << RESET << std::endl;
|
||||
}
|
||||
|
||||
// 4. GET again to verify upsert using high-level method
|
||||
brpc::CouchbaseOperations::Result coll_get2_result =
|
||||
couchbase_ops.get(coll_key, collection_name);
|
||||
if (coll_get2_result.success) {
|
||||
std::cout << GREEN
|
||||
<< "Collection GET(after upsert) value=" << coll_get2_result.value
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// 5. DELETE from collection using high-level method
|
||||
brpc::CouchbaseOperations::Result coll_del_result =
|
||||
couchbase_ops.delete_(coll_key, collection_name);
|
||||
if (coll_del_result.success) {
|
||||
std::cout << GREEN << "Collection DELETE success" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED
|
||||
<< "Collection DELETE failed: " << coll_del_result.error_message
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Pipeline Operations Demo
|
||||
// ------------------------------------------------------------------
|
||||
std::cout << GREEN << "\n=== Pipeline Operations Demo ===" << RESET
|
||||
<< std::endl;
|
||||
|
||||
// Begin a new pipeline
|
||||
if (!couchbase_ops.beginPipeline()) {
|
||||
std::cout << RED << "Failed to begin pipeline" << RESET << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Pipeline started. Adding multiple operations..." << std::endl;
|
||||
|
||||
// Add multiple operations to the pipeline
|
||||
std::string pipeline_key1 = "pipeline::doc1";
|
||||
std::string pipeline_key2 = "pipeline::doc2";
|
||||
std::string pipeline_key3 = "pipeline::doc3";
|
||||
std::string pipeline_value1 = R"({"operation": "pipeline_add", "id": 1})";
|
||||
std::string pipeline_value2 = R"({"operation": "pipeline_upsert", "id": 2})";
|
||||
std::string pipeline_value3 = R"({"operation": "pipeline_add", "id": 3})";
|
||||
|
||||
// Pipeline operations - all prepared but not yet executed
|
||||
bool pipeline_success = true;
|
||||
pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::ADD, pipeline_key1, pipeline_value1);
|
||||
pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::UPSERT, pipeline_key2, pipeline_value2);
|
||||
pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::ADD, pipeline_key3, pipeline_value3);
|
||||
pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::GET, pipeline_key1);
|
||||
pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::GET, pipeline_key2);
|
||||
|
||||
if (!pipeline_success) {
|
||||
std::cout << RED << "Failed to add operations to pipeline" << RESET
|
||||
<< std::endl;
|
||||
couchbase_ops.clearPipeline();
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Added " << couchbase_ops.getPipelineSize()
|
||||
<< " operations to pipeline" << std::endl;
|
||||
|
||||
// Execute all operations in a single network call
|
||||
std::cout << "Executing pipeline operations..." << std::endl;
|
||||
std::vector<brpc::CouchbaseOperations::Result> pipeline_results =
|
||||
couchbase_ops.executePipeline();
|
||||
|
||||
// Process results in order
|
||||
std::cout << GREEN << "Pipeline execution completed. Results:" << RESET
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < pipeline_results.size(); ++i) {
|
||||
const auto& result = pipeline_results[i];
|
||||
if (result.success) {
|
||||
if (!result.value.empty()) {
|
||||
std::cout << GREEN << " Operation " << (i + 1)
|
||||
<< " SUCCESS - Value: " << result.value << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << GREEN << " Operation " << (i + 1) << " SUCCESS" << RESET
|
||||
<< std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << RED << " Operation " << (i + 1)
|
||||
<< " FAILED: " << result.error_message << RESET << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Demonstrate pipeline with collection operations
|
||||
std::cout << GREEN << "\n=== Pipeline with Collection Operations ===" << RESET
|
||||
<< std::endl;
|
||||
|
||||
if (!couchbase_ops.beginPipeline()) {
|
||||
std::cout << RED << "Failed to begin collection pipeline" << RESET
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string coll_pipeline_key1 = "coll_pipeline::doc1";
|
||||
std::string coll_pipeline_key2 = "coll_pipeline::doc2";
|
||||
std::string coll_pipeline_value1 =
|
||||
R"({"collection_operation": "pipeline_add", "id": 1})";
|
||||
std::string coll_pipeline_value2 =
|
||||
R"({"collection_operation": "pipeline_upsert", "id": 2})";
|
||||
|
||||
// Add collection-scoped operations to pipeline
|
||||
bool coll_pipeline_success = true;
|
||||
coll_pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::ADD, coll_pipeline_key1, coll_pipeline_value1,
|
||||
collection_name);
|
||||
coll_pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::UPSERT, coll_pipeline_key2,
|
||||
coll_pipeline_value2, collection_name);
|
||||
coll_pipeline_success &= couchbase_ops.pipelineRequest(
|
||||
brpc::CouchbaseOperations::GET, coll_pipeline_key1, "", collection_name);
|
||||
coll_pipeline_success &=
|
||||
couchbase_ops.pipelineRequest(brpc::CouchbaseOperations::DELETE,
|
||||
coll_pipeline_key1, "", collection_name);
|
||||
|
||||
if (!coll_pipeline_success) {
|
||||
std::cout << RED << "Failed to add collection operations to pipeline"
|
||||
<< RESET << std::endl;
|
||||
couchbase_ops.clearPipeline();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Execute collection pipeline
|
||||
std::vector<brpc::CouchbaseOperations::Result> coll_pipeline_results =
|
||||
couchbase_ops.executePipeline();
|
||||
|
||||
std::cout << GREEN
|
||||
<< "Collection pipeline execution completed. Results:" << RESET
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < coll_pipeline_results.size(); ++i) {
|
||||
const auto& result = coll_pipeline_results[i];
|
||||
if (result.success) {
|
||||
if (!result.value.empty()) {
|
||||
std::cout << GREEN << " Collection Operation " << (i + 1)
|
||||
<< " SUCCESS - Value: " << result.value << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << GREEN << " Collection Operation " << (i + 1) << " SUCCESS"
|
||||
<< RESET << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << RED << " Collection Operation " << (i + 1)
|
||||
<< " FAILED: " << result.error_message << RESET << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up remaining pipeline documents
|
||||
std::cout << GREEN << "\n=== Cleanup Pipeline Demo ===" << RESET << std::endl;
|
||||
if (couchbase_ops.beginPipeline()) {
|
||||
couchbase_ops.pipelineRequest(brpc::CouchbaseOperations::DELETE,
|
||||
pipeline_key1);
|
||||
couchbase_ops.pipelineRequest(brpc::CouchbaseOperations::DELETE,
|
||||
pipeline_key2);
|
||||
couchbase_ops.pipelineRequest(brpc::CouchbaseOperations::DELETE,
|
||||
pipeline_key3);
|
||||
couchbase_ops.pipelineRequest(brpc::CouchbaseOperations::DELETE,
|
||||
coll_pipeline_key2, "", collection_name);
|
||||
|
||||
std::vector<brpc::CouchbaseOperations::Result> cleanup_results =
|
||||
couchbase_ops.executePipeline();
|
||||
std::cout << "Cleanup completed (" << cleanup_results.size()
|
||||
<< " operations)" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GREEN
|
||||
<< "\n=== All operations completed successfully! ===" << RESET
|
||||
<< std::endl;
|
||||
}
|
||||
int main() {
|
||||
// Create CouchbaseOperations instance for high-level operations
|
||||
brpc::CouchbaseOperations couchbase_ops;
|
||||
|
||||
// std::cout << GREEN << "Using high-level CouchbaseOperations interface"
|
||||
// << RESET << std::endl;
|
||||
|
||||
// Ask username and password for authentication
|
||||
std::string username = "Administrator";
|
||||
std::string password = "password";
|
||||
while (username.empty() || password.empty()) {
|
||||
std::cout << "Enter Couchbase username: ";
|
||||
std::cin >> username;
|
||||
if (username.empty()) {
|
||||
std::cout << "Username cannot be empty. Please enter again." << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "Enter Couchbase password: ";
|
||||
std::cin >> password;
|
||||
if (password.empty()) {
|
||||
std::cout << "Password cannot be empty. Please enter again." << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Use high-level authentication method
|
||||
// when connecting to capella use couchbase_ops.authenticate(username,
|
||||
// password, FLAGS_server, true, "path/to/cert.txt");
|
||||
brpc::CouchbaseOperations::Result auth_result =
|
||||
couchbase_ops.authenticate(username, password, FLAGS_server, "testing");
|
||||
if (!auth_result.success) {
|
||||
LOG(ERROR) << "Authentication failed: " << auth_result.error_message;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout
|
||||
<< GREEN
|
||||
<< "Authentication successful, proceeding with Couchbase operations..."
|
||||
<< RESET << std::endl;
|
||||
|
||||
performOperations(couchbase_ops);
|
||||
|
||||
// Change bucket Selection
|
||||
std::string bucket_name = "testing";
|
||||
while (bucket_name.empty()) {
|
||||
std::cout << "Enter Couchbase bucket name: ";
|
||||
std::cin >> bucket_name;
|
||||
if (bucket_name.empty()) {
|
||||
std::cout << "Bucket name cannot be empty. Please enter again."
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Use high-level bucket selection method
|
||||
brpc::CouchbaseOperations::Result bucket_result =
|
||||
couchbase_ops.selectBucket(bucket_name);
|
||||
if (!bucket_result.success) {
|
||||
LOG(ERROR) << "Bucket selection failed: " << bucket_result.error_message;
|
||||
return -1;
|
||||
} else {
|
||||
std::cout << GREEN << "Bucket Selection Successful" << RESET << std::endl;
|
||||
}
|
||||
// Add operation using high-level method
|
||||
performOperations(couchbase_ops);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <brpc/couchbase.h>
|
||||
#include <bthread/bthread.h>
|
||||
#include <butil/logging.h>
|
||||
#include <butil/string_printf.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// ANSI color codes
|
||||
#define GREEN "\033[32m"
|
||||
#define RED "\033[31m"
|
||||
#define BLUE "\033[34m"
|
||||
#define YELLOW "\033[33m"
|
||||
#define CYAN "\033[36m"
|
||||
#define RESET "\033[0m"
|
||||
|
||||
const int NUM_THREADS = 20;
|
||||
const int THREADS_PER_BUCKET = 5;
|
||||
|
||||
// Simple global config
|
||||
struct {
|
||||
std::string username = "Administrator";
|
||||
std::string password = "password";
|
||||
std::vector<std::string> bucket_names = {"t0", "t1", "t2", "t3"};
|
||||
} g_config;
|
||||
|
||||
// Simple thread statistics
|
||||
struct ThreadStats {
|
||||
std::atomic<int> operations_attempted{0};
|
||||
std::atomic<int> operations_successful{0};
|
||||
std::atomic<int> operations_failed{0};
|
||||
|
||||
void reset() {
|
||||
operations_attempted = 0;
|
||||
operations_successful = 0;
|
||||
operations_failed = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Global statistics
|
||||
struct GlobalStats {
|
||||
ThreadStats total;
|
||||
std::vector<ThreadStats> per_thread_stats;
|
||||
|
||||
GlobalStats() : per_thread_stats(NUM_THREADS) {}
|
||||
|
||||
void aggregate_stats() {
|
||||
total.reset();
|
||||
for (const auto& stats : per_thread_stats) {
|
||||
total.operations_attempted += stats.operations_attempted.load();
|
||||
total.operations_successful += stats.operations_successful.load();
|
||||
total.operations_failed += stats.operations_failed.load();
|
||||
}
|
||||
}
|
||||
} g_stats;
|
||||
|
||||
// Simple thread arguments
|
||||
struct ThreadArgs {
|
||||
int thread_id;
|
||||
int bucket_id;
|
||||
std::string bucket_name;
|
||||
brpc::CouchbaseOperations* couchbase_ops;
|
||||
ThreadStats* stats;
|
||||
};
|
||||
|
||||
// Simple CRUD operations on default collection
|
||||
void perform_crud_operations_default(brpc::CouchbaseOperations& couchbase_ops,
|
||||
const std::string& base_key,
|
||||
ThreadStats* stats) {
|
||||
std::string key = base_key + "_default";
|
||||
std::string value = butil::string_printf(
|
||||
R"({"thread_id": %d, "collection": "default"})", (int)bthread_self());
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// UPSERT
|
||||
brpc::CouchbaseOperations::Result result = couchbase_ops.upsert(key, value);
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// GET
|
||||
result = couchbase_ops.get(key);
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
return;
|
||||
}
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// DELETE
|
||||
result = couchbase_ops.delete_(key);
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple CRUD operations on col1 collection
|
||||
void perform_crud_operations_col1(brpc::CouchbaseOperations& couchbase_ops,
|
||||
const std::string& base_key,
|
||||
ThreadStats* stats) {
|
||||
std::string key = base_key + "_col1";
|
||||
std::string value = butil::string_printf(
|
||||
R"({"thread_id": %d, "collection": "col1"})", (int)bthread_self());
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// UPSERT
|
||||
brpc::CouchbaseOperations::Result result =
|
||||
couchbase_ops.upsert(key, value, "col1");
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
std::cout << "UPSERT failed: " << result.error_message << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// GET
|
||||
result = couchbase_ops.get(key, "col1");
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
std::cout << "GET failed: " << result.error_message << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
stats->operations_attempted++;
|
||||
|
||||
// DELETE
|
||||
result = couchbase_ops.delete_(key, "col1");
|
||||
if (result.success) {
|
||||
stats->operations_successful++;
|
||||
} else {
|
||||
stats->operations_failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple thread worker function
|
||||
void* thread_worker(void* arg) {
|
||||
ThreadArgs* args = static_cast<ThreadArgs*>(arg);
|
||||
|
||||
std::cout << CYAN << "Thread " << args->thread_id << " starting on bucket "
|
||||
<< args->bucket_name << RESET << std::endl;
|
||||
|
||||
// Create CouchbaseOperations instance for this thread
|
||||
brpc::CouchbaseOperations couchbase_ops;
|
||||
|
||||
// Authentication
|
||||
brpc::CouchbaseOperations::Result auth_result = couchbase_ops.authenticate(
|
||||
g_config.username, g_config.password, "127.0.0.1:11210", args->bucket_name);
|
||||
// for SSL authentication use below line instead
|
||||
// brpc::CouchbaseOperations::Result auth_result = couchbase_ops.authenticateSSL(username, password, "127.0.0.1:11210", args->bucket_name, "/path/to/cert.txt");
|
||||
|
||||
if (!auth_result.success) {
|
||||
std::cout << RED << "Thread " << args->thread_id << ": Auth failed - "
|
||||
<< auth_result.error_message << RESET << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// // Select bucket
|
||||
// brpc::CouchbaseOperations::Result bucket_result =
|
||||
// couchbase_ops.selectBucket(args->bucket_name);
|
||||
|
||||
// if (!bucket_result.success) {
|
||||
// std::cout << RED << "Thread " << args->thread_id
|
||||
// << ": Bucket selection failed - " << bucket_result.error_message
|
||||
// << RESET << std::endl;
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
std::cout << GREEN << "Thread " << args->thread_id << " connected to bucket "
|
||||
<< args->bucket_name << RESET << std::endl;
|
||||
|
||||
// Perform operations - 10 times on default collection, 10 times on col1
|
||||
// collection
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
std::string base_key =
|
||||
butil::string_printf("thread_%d_op_%d", args->thread_id, i);
|
||||
|
||||
// CRUD operations on default collection
|
||||
perform_crud_operations_default(couchbase_ops, base_key, args->stats);
|
||||
|
||||
// CRUD operations on col1 collection
|
||||
perform_crud_operations_col1(couchbase_ops, base_key, args->stats);
|
||||
|
||||
// Small delay between operations
|
||||
bthread_usleep(10000); // 10ms
|
||||
}
|
||||
|
||||
int successful = args->stats->operations_successful.load();
|
||||
int attempted = args->stats->operations_attempted.load();
|
||||
int failed = args->stats->operations_failed.load();
|
||||
|
||||
std::cout << GREEN << "Thread " << args->thread_id
|
||||
<< " completed: " << successful << "/" << attempted
|
||||
<< " operations successful, " << failed << " failed" << RESET
|
||||
<< std::endl;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* shared_object_thread_worker(void *arg){
|
||||
ThreadArgs* shared_args = static_cast<ThreadArgs*>(arg);
|
||||
brpc::CouchbaseOperations* shared_couchbase_ops = shared_args->couchbase_ops;
|
||||
// Perform operations - 10 times on default collection, 10 times on col1
|
||||
// collection
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
std::string base_key =
|
||||
butil::string_printf("shared_thread_op_%d_thread_id_%d", i, shared_args->thread_id);
|
||||
// CRUD operations on default collection
|
||||
perform_crud_operations_default(*shared_couchbase_ops, base_key, shared_args->stats);
|
||||
// CRUD operations on col1 collection
|
||||
perform_crud_operations_col1(*shared_couchbase_ops, base_key, shared_args->stats);
|
||||
// Small delay between operations
|
||||
bthread_usleep(10000); // 10ms
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Print simple statistics
|
||||
void print_stats() {
|
||||
g_stats.aggregate_stats();
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << CYAN << "=== TEST RESULTS ===" << RESET << std::endl;
|
||||
|
||||
int total_attempted = g_stats.total.operations_attempted.load();
|
||||
int total_successful = g_stats.total.operations_successful.load();
|
||||
int total_failed = g_stats.total.operations_failed.load();
|
||||
|
||||
double success_rate = total_attempted > 0
|
||||
? (double)total_successful / total_attempted * 100.0
|
||||
: 0.0;
|
||||
|
||||
std::cout << GREEN << "Overall Performance:" << RESET << std::endl;
|
||||
std::cout << " Total Operations: " << total_attempted << std::endl;
|
||||
std::cout << " Successful: " << total_successful << " (" << success_rate
|
||||
<< "%)" << std::endl;
|
||||
std::cout << " Failed: " << total_failed << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
// Per-thread breakdown
|
||||
std::cout << YELLOW << "Per-Thread Performance:" << RESET << std::endl;
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
const auto& stats = g_stats.per_thread_stats[i];
|
||||
int attempted = stats.operations_attempted.load();
|
||||
int successful = stats.operations_successful.load();
|
||||
int failed = stats.operations_failed.load();
|
||||
|
||||
std::cout << " Thread " << i << ": " << attempted << " ops, " << successful
|
||||
<< " success, " << failed << " failed" << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::cout << GREEN << "Starting Simple Multithreaded Couchbase Client"
|
||||
<< RESET << std::endl;
|
||||
std::cout
|
||||
<< YELLOW
|
||||
<< "20 threads: 5 per bucket (testing0, testing1, testing2, testing3)"
|
||||
<< RESET << std::endl;
|
||||
std::cout << BLUE
|
||||
<< "Each thread performs CRUD operations on default collection and "
|
||||
"col1 collection"
|
||||
<< RESET << std::endl;
|
||||
|
||||
// Create threads and arguments
|
||||
std::vector<bthread_t> threads(NUM_THREADS);
|
||||
std::vector<ThreadArgs> args(NUM_THREADS);
|
||||
|
||||
// Assign threads to buckets
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
args[i].thread_id = i;
|
||||
args[i].bucket_id = i / THREADS_PER_BUCKET;
|
||||
args[i].bucket_name = g_config.bucket_names[args[i].bucket_id];
|
||||
args[i].stats = &g_stats.per_thread_stats[i];
|
||||
}
|
||||
|
||||
// Print thread assignments
|
||||
std::cout << "Thread Assignments:" << RESET << std::endl;
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
std::cout << "Thread " << i << " -> Bucket: " << args[i].bucket_name
|
||||
<< std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Start all threads
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
if (bthread_start_background(&threads[i], NULL, thread_worker, &args[i]) !=
|
||||
0) {
|
||||
std::cout << RED << "Failed to create thread " << i << RESET << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << GREEN << "All 20 threads started!" << RESET << std::endl;
|
||||
|
||||
// Wait for all threads to complete
|
||||
std::cout << YELLOW << "Waiting for all threads to complete..." << RESET
|
||||
<< std::endl;
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
bthread_join(threads[i], NULL);
|
||||
}
|
||||
|
||||
std::cout << GREEN << "All threads completed!" << RESET << std::endl;
|
||||
// create a shared CouchbaseOperations instance
|
||||
brpc::CouchbaseOperations shared_couchbase_ops;
|
||||
brpc::CouchbaseOperations::Result result;
|
||||
result = shared_couchbase_ops.authenticate(
|
||||
g_config.username, g_config.password, "127.0.0.1:11210", "t0");
|
||||
if(result.success){
|
||||
std::cout << GREEN << "Shared CouchbaseOperations instance authenticated successfully!" << RESET << std::endl;
|
||||
} else {
|
||||
std::cout << RED << "Shared CouchbaseOperations instance authentication failed: " << result.error_message << RESET << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_THREADS; ++i) {
|
||||
args[i].thread_id = i;
|
||||
args[i].couchbase_ops = &shared_couchbase_ops;
|
||||
args[i].bucket_id = 0;
|
||||
args[i].bucket_name = "t0";
|
||||
// args[i].stats = &g_stats.per_thread_stats[i];
|
||||
}
|
||||
|
||||
for(int i = 0; i < NUM_THREADS; ++i){
|
||||
if (bthread_start_background(&threads[i], NULL, shared_object_thread_worker, &args[i]) !=
|
||||
0) {
|
||||
std::cout << RED << "Failed to create shared object thread " << i << RESET << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < NUM_THREADS; ++i){
|
||||
bthread_join(threads[i], NULL);
|
||||
}
|
||||
std::cout << GREEN << "All shared object threads completed!" << RESET << std::endl;
|
||||
|
||||
// Print statistics
|
||||
print_stats();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <brpc/couchbase.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// ANSI color codes for console output
|
||||
#define GREEN "\033[32m"
|
||||
#define RED "\033[31m"
|
||||
#define RESET "\033[0m"
|
||||
|
||||
int main() {
|
||||
// traditional bRPC Couchbase client
|
||||
brpc::Channel channel;
|
||||
brpc::ChannelOptions options;
|
||||
options.protocol = brpc::PROTOCOL_COUCHBASE;
|
||||
options.connection_type = "single";
|
||||
options.timeout_ms = 1000; // 1 second
|
||||
options.max_retry = 3;
|
||||
if (channel.Init("localhost:11210", &options) != 0) {
|
||||
LOG(ERROR) << "Failed to initialize channel";
|
||||
return -1;
|
||||
}
|
||||
brpc::Controller cntl;
|
||||
brpc::CouchbaseOperations::CouchbaseRequest req;
|
||||
brpc::CouchbaseOperations::CouchbaseResponse res;
|
||||
uint64_t cas;
|
||||
req.authenticateRequest("Administrator", "password");
|
||||
channel.CallMethod(NULL, &cntl, &req, &res, NULL);
|
||||
if (cntl.Failed()) {
|
||||
LOG(ERROR) << "Unable to authenticate: Something went wrong"
|
||||
<< cntl.ErrorText();
|
||||
return -1;
|
||||
} else {
|
||||
if (res.popHello(&cas) && res.popAuthenticate(&cas)) {
|
||||
std::cout << "Traditional bRPC Couchbase Client Authentication Successful"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << "Client Authentication Failed with status code: " << std::hex
|
||||
<< res._status_code << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cntl.Reset();
|
||||
// clearing request and response
|
||||
|
||||
req.Clear();
|
||||
res.Clear();
|
||||
req.selectBucketRequest("testing");
|
||||
channel.CallMethod(NULL, &cntl, &req, &res, NULL);
|
||||
if (cntl.Failed()) {
|
||||
LOG(ERROR) << "Unable to select bucket: Something went wrong"
|
||||
<< cntl.ErrorText();
|
||||
return -1;
|
||||
} else {
|
||||
if (res.popSelectBucket(&cas)) {
|
||||
std::cout
|
||||
<< "Traditional bRPC Couchbase Client Bucket Selection Successful"
|
||||
<< std::endl;
|
||||
} else {
|
||||
// the status code will be updated only after you do
|
||||
// popFunctionName(param).
|
||||
std::cout << "Traditional bRPC Couchbase Client Bucket Selection Failed "
|
||||
"with status code: "
|
||||
<< std::hex << res._status_code << std::endl;
|
||||
std::cout << "Error Message: " << res.lastError() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cntl.Reset();
|
||||
// clearing request and response
|
||||
|
||||
req.Clear();
|
||||
res.Clear();
|
||||
req.addRequest(
|
||||
"sample_key",
|
||||
R"({"name": "John Doe", "age": 30, "email": "john@example.com"})",
|
||||
0 /*flags*/, 0 /*exptime*/, 0 /*cas*/);
|
||||
channel.CallMethod(NULL, &cntl, &req, &res, NULL);
|
||||
if (cntl.Failed()) {
|
||||
LOG(ERROR) << "Unable to add key-value: Something went wrong"
|
||||
<< cntl.ErrorText();
|
||||
return -1;
|
||||
} else {
|
||||
if (res.popAdd(&cas)) {
|
||||
std::cout
|
||||
<< "Traditional bRPC Couchbase Client Key-Value Addition Successful"
|
||||
<< std::endl;
|
||||
} else {
|
||||
// the status code will be updated only after you do
|
||||
// popFunctionName(param).
|
||||
std::cout << "Traditional bRPC Couchbase Client Key-Value Addition "
|
||||
"Failed with status code: "
|
||||
<< std::hex << res._status_code << std::endl;
|
||||
std::cout << "Error Message: " << res.lastError() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cntl.Reset();
|
||||
|
||||
// clearing request and response before doing a getRequest
|
||||
req.Clear();
|
||||
res.Clear();
|
||||
req.getRequest("sample_key");
|
||||
channel.CallMethod(NULL, &cntl, &req, &res, NULL);
|
||||
if (cntl.Failed()) {
|
||||
LOG(ERROR) << "Unable to get value for key: Something went wrong"
|
||||
<< cntl.ErrorText();
|
||||
return -1;
|
||||
} else {
|
||||
std::string value;
|
||||
uint32_t flags;
|
||||
if (res.popGet(&value, &flags, &cas)) {
|
||||
std::cout
|
||||
<< "Traditional bRPC Couchbase Client Key-Value Retrieval Successful"
|
||||
<< std::endl;
|
||||
std::cout << "Retrieved Value: " << value << std::endl;
|
||||
} else {
|
||||
// note the status code will be updated only after you do
|
||||
// popFunctionName(param).
|
||||
std::cout << "Traditional bRPC Couchbase Client Key-Value Retrieval "
|
||||
"Failed with status code: "
|
||||
<< std::hex << res._status_code << std::endl;
|
||||
std::cout << "Error Message: " << res.lastError() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cntl.Reset();
|
||||
// clearing request and response before doing a deleteRequest
|
||||
|
||||
req.Clear();
|
||||
res.Clear();
|
||||
req.deleteRequest("sample_key");
|
||||
channel.CallMethod(NULL, &cntl, &req, &res, NULL);
|
||||
if (cntl.Failed()) {
|
||||
LOG(ERROR) << "Unable to delete key-value: Something went wrong"
|
||||
<< cntl.ErrorText();
|
||||
return -1;
|
||||
} else {
|
||||
if (res.popDelete()) {
|
||||
std::cout
|
||||
<< "Traditional bRPC Couchbase Client Key-Value Deletion Successful"
|
||||
<< std::endl;
|
||||
} else {
|
||||
// the status code will be updated only after you do
|
||||
// popFunctionName(param).
|
||||
std::cout << "Traditional bRPC Couchbase Client Key-Value Deletion "
|
||||
"Failed with status code: "
|
||||
<< std::hex << res._status_code << std::endl;
|
||||
std::cout << "Error Message: " << res.lastError() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user