Commit 56179c97 authored by Vaastav Anand's avatar Vaastav Anand

Add instrumentation for MovieReviewHandler

parent 0e12f212
...@@ -122,7 +122,7 @@ void MovieIdHandler::UploadMovieId( ...@@ -122,7 +122,7 @@ void MovieIdHandler::UploadMovieId(
LOG(debug) << "Get movie_id " << movie_id_mmc LOG(debug) << "Get movie_id " << movie_id_mmc
<< " cache hit from Memcached"; << " cache hit from Memcached";
movie_id_str = std::string(movie_id_mmc); movie_id_str = std::string(movie_id_mmc);
XTRACE("Cache hit in Memcached for movie_id " + std::to_string(movie_id_mmc)); XTRACE("Cache hit in Memcached for movie_id " + std::string(movie_id_mmc));
free(movie_id_mmc); free(movie_id_mmc);
} }
...@@ -231,7 +231,7 @@ void MovieIdHandler::UploadMovieId( ...@@ -231,7 +231,7 @@ void MovieIdHandler::UploadMovieId(
if (memcached_rc != MEMCACHED_SUCCESS) { if (memcached_rc != MEMCACHED_SUCCESS) {
LOG(warning) << "Failed to set movie_id to Memcached: " LOG(warning) << "Failed to set movie_id to Memcached: "
<< memcached_strerror(memcached_client, memcached_rc); << memcached_strerror(memcached_client, memcached_rc);
XTRACE("Failed to set movie_id to Memcached: " + memcached_strerror(memcached_client, memcached_rc)); XTRACE("Failed to set movie_id to Memcached: " + std::string(memcached_strerror(memcached_client, memcached_rc)));
} }
memcached_pool_push(_memcached_client_pool, memcached_client); memcached_pool_push(_memcached_client_pool, memcached_client);
}); });
......
...@@ -169,7 +169,7 @@ void MovieInfoHandler::WriteMovieInfo( ...@@ -169,7 +169,7 @@ void MovieInfoHandler::WriteMovieInfo(
if (!plotinsert) { if (!plotinsert) {
LOG(error) << "Error: Failed to insert movie-info to MongoDB: " LOG(error) << "Error: Failed to insert movie-info to MongoDB: "
<< error.message; << error.message;
XTRACE("Error: Failed to insert movie-info to MongoDB " + error.message); XTRACE("Error: Failed to insert movie-info to MongoDB " + std::string(error.message));
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = error.message; se.message = error.message;
...@@ -250,7 +250,7 @@ void MovieInfoHandler::ReadMovieInfo( ...@@ -250,7 +250,7 @@ void MovieInfoHandler::ReadMovieInfo(
if (movie_info_mmc) { if (movie_info_mmc) {
LOG(debug) << "Get movie-info " << movie_id << " cache hit from Memcached"; LOG(debug) << "Get movie-info " << movie_id << " cache hit from Memcached";
XTRACE("Cache hit in Memcached for movie " + std::to_string(movie_id)); XTRACE("Cache hit in Memcached for movie " + movie_id);
json movie_info_json = json::parse(std::string( json movie_info_json = json::parse(std::string(
movie_info_mmc, movie_info_mmc + movie_info_mmc_size)); movie_info_mmc, movie_info_mmc + movie_info_mmc_size));
_return.movie_id = movie_info_json["movie_id"]; _return.movie_id = movie_info_json["movie_id"];
...@@ -324,7 +324,7 @@ void MovieInfoHandler::ReadMovieInfo( ...@@ -324,7 +324,7 @@ void MovieInfoHandler::ReadMovieInfo(
throw se; throw se;
} else { } else {
LOG(warning) << "Movie_id: " << movie_id << " doesn't exist in MongoDB"; LOG(warning) << "Movie_id: " << movie_id << " doesn't exist in MongoDB";
XTRACE("Movie_id: " + std::to_string(movie_id) + " doesn't exist in MongoDB"); XTRACE("Movie_id: " + movie_id + " doesn't exist in MongoDB");
bson_destroy(query); bson_destroy(query);
mongoc_cursor_destroy(cursor); mongoc_cursor_destroy(cursor);
mongoc_collection_destroy(collection); mongoc_collection_destroy(collection);
...@@ -336,7 +336,7 @@ void MovieInfoHandler::ReadMovieInfo( ...@@ -336,7 +336,7 @@ void MovieInfoHandler::ReadMovieInfo(
} }
} else { } else {
LOG(debug) << "Movie_id: " << movie_id << " found in MongoDB"; LOG(debug) << "Movie_id: " << movie_id << " found in MongoDB";
XTRACE("Movie_id: " + std::to_string(movie_id) + " found in MongoDB"); XTRACE("Movie_id: " + movie_id + " found in MongoDB");
auto movie_info_json_char = bson_as_json(doc, nullptr); auto movie_info_json_char = bson_as_json(doc, nullptr);
json movie_info_json = json::parse(movie_info_json_char); json movie_info_json = json::parse(movie_info_json_char);
_return.movie_id = movie_info_json["movie_id"]; _return.movie_id = movie_info_json["movie_id"];
...@@ -499,7 +499,7 @@ void MovieInfoHandler::UpdateRating( ...@@ -499,7 +499,7 @@ void MovieInfoHandler::UpdateRating(
if (!updated) { if (!updated) {
LOG(error) << "Failed to update rating for movie " << movie_id LOG(error) << "Failed to update rating for movie " << movie_id
<< " to MongoDB: " << error.message; << " to MongoDB: " << error.message;
XTRACE("Failed to update rating for movie " + std::to_string(movie_id) + " to MongoDB"); XTRACE("Failed to update rating for movie " + movie_id + " to MongoDB");
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = "Failed to update rating for movie " + movie_id + se.message = "Failed to update rating for movie " + movie_id +
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include "../ClientPool.h" #include "../ClientPool.h"
#include "../RedisClient.h" #include "../RedisClient.h"
#include "../ThriftClient.h" #include "../ThriftClient.h"
#include <xtrace/xtrace.h>
#include <xtrace/baggage.h>
namespace media_service { namespace media_service {
class MovieReviewHandler : public MovieReviewServiceIf { class MovieReviewHandler : public MovieReviewServiceIf {
...@@ -51,6 +53,16 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -51,6 +53,16 @@ void MovieReviewHandler::UploadMovieReview(
int64_t timestamp, int64_t timestamp,
const std::map<std::string, std::string> & carrier) { const std::map<std::string, std::string> & carrier) {
std::map<std::string, std::string>::const_iterator baggage_it = carrier.find("baggage");
if (baggage_it != carrier.end()) {
SET_CURRENT_BAGGAGE(Baggage::deserialize(baggage_it->second));
}
if (!XTrace::IsTracing()) {
XTrace::StartTrace("MovieReviewHandler");
}
XTRACE("MovieReviewHandler::UploadMovieReview", {{"RequestID", std::to_string(req_id)}});
// Initialize a span // Initialize a span
TextMapReader reader(carrier); TextMapReader reader(carrier);
std::map<std::string, std::string> writer_text_map; std::map<std::string, std::string> writer_text_map;
...@@ -67,6 +79,7 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -67,6 +79,7 @@ void MovieReviewHandler::UploadMovieReview(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = "Failed to pop a client from MongoDB pool"; se.message = "Failed to pop a client from MongoDB pool";
XTRACE("Failed to pop a client from MongoDB pool");
throw se; throw se;
} }
...@@ -76,16 +89,19 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -76,16 +89,19 @@ void MovieReviewHandler::UploadMovieReview(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = "Failed to create collection movie-review from DB movie-review"; se.message = "Failed to create collection movie-review from DB movie-review";
XTRACE("Failed to create collection movie-review from DB movie-review");
mongoc_client_pool_push(_mongodb_client_pool, mongodb_client); mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
throw se; throw se;
} }
bson_t *query = bson_new(); bson_t *query = bson_new();
BSON_APPEND_UTF8(query, "movie_id", movie_id.c_str()); BSON_APPEND_UTF8(query, "movie_id", movie_id.c_str());
XTRACE("MongoFindMovie start");
auto find_span = opentracing::Tracer::Global()->StartSpan( auto find_span = opentracing::Tracer::Global()->StartSpan(
"MongoFindMovie", {opentracing::ChildOf(&span->context())}); "MongoFindMovie", {opentracing::ChildOf(&span->context())});
mongoc_cursor_t *cursor = mongoc_collection_find_with_opts( mongoc_cursor_t *cursor = mongoc_collection_find_with_opts(
collection, query, nullptr, nullptr); collection, query, nullptr, nullptr);
XTRACE("MongoFindMovie finish");
const bson_t *doc; const bson_t *doc;
bool found = mongoc_cursor_next(cursor, &doc); bool found = mongoc_cursor_next(cursor, &doc);
if (!found) { if (!found) {
...@@ -96,14 +112,17 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -96,14 +112,17 @@ void MovieReviewHandler::UploadMovieReview(
"timestamp", BCON_INT64(timestamp), "}", "]" "timestamp", BCON_INT64(timestamp), "}", "]"
); );
bson_error_t error; bson_error_t error;
XTRACE("MongoInsert start");
auto insert_span = opentracing::Tracer::Global()->StartSpan( auto insert_span = opentracing::Tracer::Global()->StartSpan(
"MongoInsert", {opentracing::ChildOf(&span->context())}); "MongoInsert", {opentracing::ChildOf(&span->context())});
bool plotinsert = mongoc_collection_insert_one( bool plotinsert = mongoc_collection_insert_one(
collection, new_doc, nullptr, nullptr, &error); collection, new_doc, nullptr, nullptr, &error);
insert_span->Finish(); insert_span->Finish();
XTRACE("MongoInsert finish");
if (!plotinsert) { if (!plotinsert) {
LOG(error) << "Failed to insert movie review of movie " << movie_id LOG(error) << "Failed to insert movie review of movie " << movie_id
<< " to MongoDB: " << error.message; << " to MongoDB: " << error.message;
XTRACE("Failed to insert movie review of movie " + movie_id + " to MongoDB");
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = error.message; se.message = error.message;
...@@ -129,15 +148,18 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -129,15 +148,18 @@ void MovieReviewHandler::UploadMovieReview(
); );
bson_error_t error; bson_error_t error;
bson_t reply; bson_t reply;
XTRACE("MongoUpdate start");
auto update_span = opentracing::Tracer::Global()->StartSpan( auto update_span = opentracing::Tracer::Global()->StartSpan(
"MongoUpdate.", {opentracing::ChildOf(&span->context())}); "MongoUpdate.", {opentracing::ChildOf(&span->context())});
bool plotupdate = mongoc_collection_find_and_modify( bool plotupdate = mongoc_collection_find_and_modify(
collection, query, nullptr, update, nullptr, false, false, collection, query, nullptr, update, nullptr, false, false,
true, &reply, &error); true, &reply, &error);
update_span->Finish(); update_span->Finish();
XTRACE("MongoUpdate finish");
if (!plotupdate) { if (!plotupdate) {
LOG(error) << "Failed to update movie-review for movie " << movie_id LOG(error) << "Failed to update movie-review for movie " << movie_id
<< " to MongoDB: " << error.message; << " to MongoDB: " << error.message;
XTRACE("Failed to update movie-review for movie " + movie_id + " to MongoDB");
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = error.message; se.message = error.message;
...@@ -162,9 +184,11 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -162,9 +184,11 @@ void MovieReviewHandler::UploadMovieReview(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_REDIS_ERROR; se.errorCode = ErrorCode::SE_REDIS_ERROR;
se.message = "Cannot connected to Redis server"; se.message = "Cannot connected to Redis server";
XTRACE("Cannot connect to Redis server");
throw se; throw se;
} }
auto redis_client = redis_client_wrapper->GetClient(); auto redis_client = redis_client_wrapper->GetClient();
XTRACE("RedisUpdate start");
auto redis_span = opentracing::Tracer::Global()->StartSpan( auto redis_span = opentracing::Tracer::Global()->StartSpan(
"RedisUpdate", {opentracing::ChildOf(&span->context())}); "RedisUpdate", {opentracing::ChildOf(&span->context())});
auto num_reviews = redis_client->zcard(movie_id); auto num_reviews = redis_client->zcard(movie_id);
...@@ -179,7 +203,10 @@ void MovieReviewHandler::UploadMovieReview( ...@@ -179,7 +203,10 @@ void MovieReviewHandler::UploadMovieReview(
} }
_redis_client_pool->Push(redis_client_wrapper); _redis_client_pool->Push(redis_client_wrapper);
redis_span->Finish(); redis_span->Finish();
XTRACE("RedisUpdate finish");
span->Finish(); span->Finish();
XTRACE("MovieReviewHandler::UpdateMovieReview finish");
DELETE_CURRENT_BAGGAGE();
} }
void MovieReviewHandler::ReadMovieReviews( void MovieReviewHandler::ReadMovieReviews(
...@@ -187,6 +214,15 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -187,6 +214,15 @@ void MovieReviewHandler::ReadMovieReviews(
const std::string& movie_id, int32_t start, int32_t stop, const std::string& movie_id, int32_t start, int32_t stop,
const std::map<std::string, std::string> & carrier) { const std::map<std::string, std::string> & carrier) {
std::map<std::string, std::string>::const_iterator baggage_it = carrier.find("baggage");
if (baggage_it != carrier.end()) {
SET_CURRENT_BAGGAGE(Baggage::deserialize(baggage_it->second));
}
if (!XTrace::IsTracing()) {
XTrace::StartTrace("MovieReviewHandler");
}
XTRACE("MovieReviewHandler::ReadMovieReviews", {{"RequestID", std::to_string(req_id)}});
// Initialize a span // Initialize a span
TextMapReader reader(carrier); TextMapReader reader(carrier);
std::map<std::string, std::string> writer_text_map; std::map<std::string, std::string> writer_text_map;
...@@ -206,20 +242,24 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -206,20 +242,24 @@ void MovieReviewHandler::ReadMovieReviews(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_REDIS_ERROR; se.errorCode = ErrorCode::SE_REDIS_ERROR;
se.message = "Cannot connected to Redis server"; se.message = "Cannot connected to Redis server";
XTRACE("Cannot connect to Redis server");
throw se; throw se;
} }
auto redis_client = redis_client_wrapper->GetClient(); auto redis_client = redis_client_wrapper->GetClient();
XTRACE("RedisFind start");
auto redis_span = opentracing::Tracer::Global()->StartSpan( auto redis_span = opentracing::Tracer::Global()->StartSpan(
"RedisFind", {opentracing::ChildOf(&span->context())}); "RedisFind", {opentracing::ChildOf(&span->context())});
auto review_ids_future = redis_client->zrevrange(movie_id, start, stop - 1); auto review_ids_future = redis_client->zrevrange(movie_id, start, stop - 1);
redis_client->commit(); redis_client->commit();
redis_span->Finish(); redis_span->Finish();
XTRACE("RedisFind finish");
cpp_redis::reply review_ids_reply; cpp_redis::reply review_ids_reply;
try { try {
review_ids_reply = review_ids_future.get(); review_ids_reply = review_ids_future.get();
} catch (...) { } catch (...) {
LOG(error) << "Failed to read review_ids from movie-review-redis"; LOG(error) << "Failed to read review_ids from movie-review-redis";
XTRACE("Failed to read review_ids from movie-review-redis");
_redis_client_pool->Push(redis_client_wrapper); _redis_client_pool->Push(redis_client_wrapper);
throw; throw;
} }
...@@ -240,6 +280,7 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -240,6 +280,7 @@ void MovieReviewHandler::ReadMovieReviews(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = "Failed to pop a client from MongoDB pool"; se.message = "Failed to pop a client from MongoDB pool";
XTRACE("Failed to pop a client from MongoDB pool");
throw se; throw se;
} }
auto collection = mongoc_client_get_collection( auto collection = mongoc_client_get_collection(
...@@ -248,6 +289,7 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -248,6 +289,7 @@ void MovieReviewHandler::ReadMovieReviews(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_MONGODB_ERROR; se.errorCode = ErrorCode::SE_MONGODB_ERROR;
se.message = "Failed to create collection movie-review from MongoDB"; se.message = "Failed to create collection movie-review from MongoDB";
XTRACE("Failed to create collection movie-review from MongoDB");
throw se; throw se;
} }
...@@ -258,11 +300,13 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -258,11 +300,13 @@ void MovieReviewHandler::ReadMovieReviews(
"$slice", "[", "$slice", "[",
BCON_INT32(0), BCON_INT32(stop), BCON_INT32(0), BCON_INT32(stop),
"]", "}", "}"); "]", "}", "}");
XTRACE("MongoFindMovieReviews start");
auto find_span = opentracing::Tracer::Global()->StartSpan( auto find_span = opentracing::Tracer::Global()->StartSpan(
"MongoFindMovieReviews", {opentracing::ChildOf(&span->context())}); "MongoFindMovieReviews", {opentracing::ChildOf(&span->context())});
mongoc_cursor_t *cursor = mongoc_collection_find_with_opts( mongoc_cursor_t *cursor = mongoc_collection_find_with_opts(
collection, query, opts, nullptr); collection, query, opts, nullptr);
find_span->Finish(); find_span->Finish();
XTRACE("MongoFindMovieReviews finish");
const bson_t *doc; const bson_t *doc;
bool found = mongoc_cursor_next(cursor, &doc); bool found = mongoc_cursor_next(cursor, &doc);
if (found) { if (found) {
...@@ -303,23 +347,28 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -303,23 +347,28 @@ void MovieReviewHandler::ReadMovieReviews(
mongoc_client_pool_push(_mongodb_client_pool, mongodb_client); mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
} }
Baggage review_baggage = BRANCH_CURRENT_BAGGAGE();
std::future<std::vector<Review>> review_future = std::async( std::future<std::vector<Review>> review_future = std::async(
std::launch::async, [&]() { std::launch::async, [&]() {
BAGGAGE(review_baggage);
auto review_client_wrapper = _review_client_pool->Pop(); auto review_client_wrapper = _review_client_pool->Pop();
if (!review_client_wrapper) { if (!review_client_wrapper) {
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_THRIFT_CONN_ERROR; se.errorCode = ErrorCode::SE_THRIFT_CONN_ERROR;
se.message = "Failed to connected to review-storage-service"; se.message = "Failed to connected to review-storage-service";
XTRACE("Failed to connect to review-storage-service");
throw se; throw se;
} }
std::vector<Review> _return_reviews; std::vector<Review> _return_reviews;
auto review_client = review_client_wrapper->GetClient(); auto review_client = review_client_wrapper->GetClient();
try { try {
writer_text_map["baggage"] = GET_CURRENT_BAGGAGE().str();
review_client->ReadReviews( review_client->ReadReviews(
_return_reviews, req_id, review_ids, writer_text_map); _return_reviews, req_id, review_ids, writer_text_map);
} catch (...) { } catch (...) {
_review_client_pool->Push(review_client_wrapper); _review_client_pool->Push(review_client_wrapper);
LOG(error) << "Failed to read review from review-storage-service"; LOG(error) << "Failed to read review from review-storage-service";
XTRACE("Failed to read review from review-storage-service");
throw; throw;
} }
_review_client_pool->Push(review_client_wrapper); _review_client_pool->Push(review_client_wrapper);
...@@ -334,9 +383,11 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -334,9 +383,11 @@ void MovieReviewHandler::ReadMovieReviews(
ServiceException se; ServiceException se;
se.errorCode = ErrorCode::SE_REDIS_ERROR; se.errorCode = ErrorCode::SE_REDIS_ERROR;
se.message = "Cannot connected to Redis server"; se.message = "Cannot connected to Redis server";
XTRACE("Cannot connect to Redis server");
throw se; throw se;
} }
redis_client = redis_client_wrapper->GetClient(); redis_client = redis_client_wrapper->GetClient();
XTRACE("RedisUpdate start");
auto redis_update_span = opentracing::Tracer::Global()->StartSpan( auto redis_update_span = opentracing::Tracer::Global()->StartSpan(
"RedisUpdate", {opentracing::ChildOf(&span->context())}); "RedisUpdate", {opentracing::ChildOf(&span->context())});
redis_client->del(std::vector<std::string>{movie_id}); redis_client->del(std::vector<std::string>{movie_id});
...@@ -345,17 +396,21 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -345,17 +396,21 @@ void MovieReviewHandler::ReadMovieReviews(
movie_id, options, redis_update_map); movie_id, options, redis_update_map);
redis_client->commit(); redis_client->commit();
redis_update_span->Finish(); redis_update_span->Finish();
XTRACE("RedisUpdate finish");
} }
try { try {
_return = review_future.get(); _return = review_future.get();
JOIN_CURRENT_BAGGAGE(review_baggage);
} catch (...) { } catch (...) {
LOG(error) << "Failed to get review from review-storage-service"; LOG(error) << "Failed to get review from review-storage-service";
XTRACE("Failed to get review from review-storage-service");
if (!redis_update_map.empty()) { if (!redis_update_map.empty()) {
try { try {
zadd_reply_future.get(); zadd_reply_future.get();
} catch (...) { } catch (...) {
LOG(error) << "Failed to Update Redis Server"; LOG(error) << "Failed to Update Redis Server";
XTRACE("Failed to Update Redis Server");
} }
_redis_client_pool->Push(redis_client_wrapper); _redis_client_pool->Push(redis_client_wrapper);
} }
...@@ -367,6 +422,7 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -367,6 +422,7 @@ void MovieReviewHandler::ReadMovieReviews(
zadd_reply_future.get(); zadd_reply_future.get();
} catch (...) { } catch (...) {
LOG(error) << "Failed to Update Redis Server"; LOG(error) << "Failed to Update Redis Server";
XTRACE("Failed to Update Redis Server");
_redis_client_pool->Push(redis_client_wrapper); _redis_client_pool->Push(redis_client_wrapper);
throw; throw;
} }
...@@ -374,6 +430,7 @@ void MovieReviewHandler::ReadMovieReviews( ...@@ -374,6 +430,7 @@ void MovieReviewHandler::ReadMovieReviews(
} }
span->Finish(); span->Finish();
XTRACE("MovieReviewHandler::ReadMovieReviews finish");
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment