initial import; removed cruft from mert's tarball, tweaked make's clean targets
git-svn-id: svn+ssh://svn.corp.yahoo.com/yahoo/yrl/labs/pnuts/code/logstore@520 8dad8b1f-cf64-0410-95b6-bcf113ffbcfe
This commit is contained in:
commit
d016498f8d
38 changed files with 10478 additions and 0 deletions
165
FwCode.h
Normal file
165
FwCode.h
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/* Copyright (C) 2008 Yahoo! Inc. All Rights Reserved. */
|
||||||
|
|
||||||
|
#ifndef __FW_CODE__H
|
||||||
|
#define __FW_CODE__H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global framework response codes.
|
||||||
|
*/
|
||||||
|
class FwCode {
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef int ResponseCode;
|
||||||
|
|
||||||
|
static const std::string unknownCodeStr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The convention here is to keep related codes grouped together, so
|
||||||
|
* that it is easier to find all existing codes for a particular
|
||||||
|
* module. Each section is given a range of 50 codes, so that adding
|
||||||
|
* a new code to an existing section won't invalidate all of the codes
|
||||||
|
* following it in the enum (causing binary incompatibility).
|
||||||
|
*/
|
||||||
|
|
||||||
|
//----------- Generic section -------------
|
||||||
|
static const ResponseCode FwOk = 0; //!< All successes
|
||||||
|
static const ResponseCode FwError = 1; //!< General error code
|
||||||
|
|
||||||
|
static const ResponseCode FwCrit = 2; //!< General critical error. could be originated by low level library to indicate some nasty error has occurred.
|
||||||
|
|
||||||
|
static const ResponseCode MdbmOpenFailed = 3; //!< Any kind of mdbm open failure
|
||||||
|
static const ResponseCode MdbmOperationFailed = 4; //!< Any store/fetch/lock from mdbm failed
|
||||||
|
static const ResponseCode NoMem = 5; //!< Out Of Memory
|
||||||
|
static const ResponseCode InvalidParam = 6; //!< Invalid parameter
|
||||||
|
static const ResponseCode NotFound = 7; //!< Fail to find the specified info; usuall returned by access methods
|
||||||
|
static const ResponseCode InvalidState = 8; //!< Invalid state
|
||||||
|
static const ResponseCode ConnReset = 9; //!< connection reset
|
||||||
|
static const ResponseCode Timeout = 10; //!< operation timed out
|
||||||
|
static const ResponseCode InvalidData = 11; //!< buffer data is invalid
|
||||||
|
static const ResponseCode BufTooSmall = 12; //!< Buffer size is smaller than required
|
||||||
|
static const ResponseCode MalformedRequest = 13; //!< Request data (like the URI) is malformed
|
||||||
|
static const ResponseCode RequestTooLarge = 14; //!< Request data (like the body) is too big
|
||||||
|
static const ResponseCode ConvertToDhtDataFailed = 15; // !< Failed convert json string to DHT::Data
|
||||||
|
static const ResponseCode ConvertFromDhtDataFailed = 16; // !< Failed to convert DHT::Data to json string
|
||||||
|
static const ResponseCode BadHexString = 17; //!< Failed to parse a hex string
|
||||||
|
static const ResponseCode ShmemCorrupted = 18; //!< A shared mem corruption has been detected.
|
||||||
|
static const ResponseCode ParseError = 19; //!< Generic parsing problem
|
||||||
|
/// If mdbm unlock fails, most of the time we want to shut off the
|
||||||
|
/// system automatically, without letting the caller know that we did
|
||||||
|
/// so. On specific instances where the caller is the FaultHandler, or
|
||||||
|
/// Oversight Fault counter (there may be other examples), we don't want
|
||||||
|
/// to do this because we want to avoid cross-dependency.
|
||||||
|
static const ResponseCode MdbmUnlockFailed = 20;
|
||||||
|
|
||||||
|
//----------- Generic section -------------
|
||||||
|
// Config
|
||||||
|
static const ResponseCode ConfigFailure = 50; //!< Failure to find or parse a config entry
|
||||||
|
|
||||||
|
//----------- UChar section -------------
|
||||||
|
// UCharUtils
|
||||||
|
static const ResponseCode UcnvOpenFailed = 100; //!< Failed to open ucnv converter for utf-8
|
||||||
|
static const ResponseCode DataNotUtf8 = 101; //!< Data is not in utf-8 format
|
||||||
|
static const ResponseCode ConvertToUCharFailed = 102; //!< Failed to convert utf-8 string to UChar string
|
||||||
|
static const ResponseCode CompileRegExFailed = 103; //!< Failed to compile the regular expression
|
||||||
|
|
||||||
|
//----------- Yca section -------------
|
||||||
|
// YcaClient
|
||||||
|
static const ResponseCode YcaOpenFailed = 150; //!< Failed to open the yca database
|
||||||
|
static const ResponseCode YcaCertInvalid = 151; //!< Validation of presented cert failed
|
||||||
|
static const ResponseCode YcaCertNotFound = 152; //!< certificate for the requested appID was not found
|
||||||
|
|
||||||
|
//----------- Broker section -------------
|
||||||
|
static const ResponseCode BrokerClientOpenFailed = 200; //!< Failed to connect to broker
|
||||||
|
static const ResponseCode UncertainPublish = 201; //!< Publish was uncertain - unknown if it happened
|
||||||
|
static const ResponseCode PublishFailed = 202; //!< Publish failed (for certain :))
|
||||||
|
static const ResponseCode SubscribeFailed = 203; //!< Failed to subscribe to a topic
|
||||||
|
static const ResponseCode NoSubscriptionFound = 204; //!< Operation on a sub failed because we (locally)
|
||||||
|
// don't know about it
|
||||||
|
static const ResponseCode RegisterFailed = 205; //!< Failed to register handler for subscription
|
||||||
|
static const ResponseCode UnsubscribeFailed = 206; //!< Failed to unsubscribe from sub
|
||||||
|
static const ResponseCode ListTopicsFailed = 207; //!< Failed to list subscribed topics
|
||||||
|
static const ResponseCode ConsumeFailed = 208; //!< Failed to consume messages for a topic
|
||||||
|
static const ResponseCode TopicInvalid = 209; //!< Topic is invalid (was usurped or ymb 'lost' it)
|
||||||
|
static const ResponseCode NoMessageDelivered = 210; //!< Call to deliver() found no messages ready
|
||||||
|
static const ResponseCode ConsumeFailedBadTopic = 211; //!< The topic is bad - our handle is bad,
|
||||||
|
// or it got usurped
|
||||||
|
static const ResponseCode ConsumeFailedBadHandle = 212; //!< Our ymb handle is bad - not usable anymore
|
||||||
|
static const ResponseCode ConsumeFailedConnectionError = 213; //!< a recoverable connection error
|
||||||
|
static const ResponseCode ConsumeFailedServerBusy = 214; //!< ymb server is having a temporary issue,
|
||||||
|
// not a failure per se
|
||||||
|
// second argument to messageProcessed()
|
||||||
|
static const ResponseCode ConsumeMessage = 215; //!< consume this message
|
||||||
|
static const ResponseCode ConsumeAndUnsubscribe = 216; //!< end this channel
|
||||||
|
// Internal to ymb implementation
|
||||||
|
static const ResponseCode YmbSubscribeTempFailure = 217; //!< A failure that might be resolved on a retry
|
||||||
|
static const ResponseCode YmbSubscribeTimedout = 218; //!< A timeout failure
|
||||||
|
static const ResponseCode YmbSubscriptionExists = 219; //!< Attempt to create a sub that already exists
|
||||||
|
static const ResponseCode NoSuchSubscription = 220; //!< Attempt to attach to a sub that does not exist
|
||||||
|
static const ResponseCode AttachNoSuchSubscription = 221; //!< Specific to attach, no subscription to attach to (not necessarily an error)
|
||||||
|
static const ResponseCode BrokerInitFailed = 222; //!< Config or allocation failed
|
||||||
|
static const ResponseCode BrokerConnectionLost = 223; //!< Lost connection to broker
|
||||||
|
static const ResponseCode BrokerFatalError = 224; //!< Generally shared mem corruption
|
||||||
|
|
||||||
|
|
||||||
|
//----------- Daemon section -------------
|
||||||
|
// Daemon
|
||||||
|
static const ResponseCode NoImpl = 250; //!< No op
|
||||||
|
static const ResponseCode Restart = 251; //!< Exit the daemon so that it is restarted right away.
|
||||||
|
// request that the daemon do a soft restart
|
||||||
|
static const ResponseCode Exit = 252; //!< Exit the daemon so that it is NOT restarted right away. A monitoring process may restart the entire system later.
|
||||||
|
static const ResponseCode StopDelivery = 253; //!< Stop delivery on the topic, returned by Broker handlers only.
|
||||||
|
static const ResponseCode RetryDelivery = 254; //!< Stop delivery on the topic but retry after sometime, returned by Broker handlers only.
|
||||||
|
|
||||||
|
//----------- Lock section -------------
|
||||||
|
// LockManager
|
||||||
|
//ALL these lock errors are handled in SuFaulHandler.cc
|
||||||
|
//Any addition to these error codes requires update to the SuFaultHandler
|
||||||
|
static const ResponseCode LockSyserr = 301; //!< System error during lock/unlock op
|
||||||
|
static const ResponseCode LockInconsis = 302; //!< Inconsistency detected in LockManager.
|
||||||
|
static const ResponseCode LockNested = 303; //!< Nested locking of same key not allowed.
|
||||||
|
static const ResponseCode LockNosuchpid = 304; //!< This pid does not hold the lock.
|
||||||
|
static const ResponseCode LockUnavail = 305; //!< Outa lock
|
||||||
|
static const ResponseCode LockInitfail = 306; //!< Initialization failure of the lock subsystem
|
||||||
|
static const ResponseCode LockInvalidarg = 307; //!< Invalid arguments to lock subsystem.
|
||||||
|
|
||||||
|
//----------- Message section -------------
|
||||||
|
//Message and Message serialization
|
||||||
|
static const ResponseCode SerializeFailed = 350; //!< Message Serialization Failed
|
||||||
|
static const ResponseCode DeserializeFailed = 351; //!< Message Deserialization failed
|
||||||
|
static const ResponseCode NoResponseCodeInMessage = 352;
|
||||||
|
|
||||||
|
//----------- Transport Errors -------------
|
||||||
|
static const ResponseCode TransportSendError = 400; //!< Curl error in communicating with other server
|
||||||
|
static const ResponseCode TransportSetHeaderFailed = 401; //!< Error in setting header in curl request
|
||||||
|
static const ResponseCode TransportCurlInitError = 402; // !< Error initializing curl handle -- should be curl specific
|
||||||
|
static const ResponseCode TransportUncertain = 403; //!< Send came back uncertain (timeout, usually)
|
||||||
|
static const ResponseCode TransportInvalidResponseBody = 404; //!< Send came back unparsable body
|
||||||
|
|
||||||
|
//----------- Apache/Web section -------------
|
||||||
|
static const ResponseCode EndOfBody = 450; //!< Normal end of incoming request body
|
||||||
|
static const ResponseCode BodyReadFailed = 451; //!< Failed reading incoming request body
|
||||||
|
static const ResponseCode BodyWriteFailed = 452; //!< Failed writing outgoing request body
|
||||||
|
static const ResponseCode EncryptionFailed = 453; //!< Failed to encrypt body or header
|
||||||
|
static const ResponseCode DecryptionFailed = 454; //!< Failed to decrypt body or header
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give back a basic, generic string description of the response code.
|
||||||
|
*
|
||||||
|
* @param rc The response code to convert.
|
||||||
|
* @return The string describing it.
|
||||||
|
*/
|
||||||
|
static std::string toString(ResponseCode rc);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* For customized vim control
|
||||||
|
* Local variables:
|
||||||
|
* tab-width: 4
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* End:
|
||||||
|
* vim600: sw=4:ts=4:et
|
||||||
|
* vim<600: sw=4:ts=4:et
|
||||||
|
*/
|
||||||
|
#endif
|
77
LogUtils.cc
Normal file
77
LogUtils.cc
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*! \file log4_util.cc
|
||||||
|
* \brief This file has the helper functions for log4cpp;
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Yahoo, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
#include <iostream>
|
||||||
|
#include <log4cpp/PropertyConfigurator.hh>
|
||||||
|
|
||||||
|
#include "LogUtils.h"
|
||||||
|
|
||||||
|
using namespace log4cpp;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// hacked link to actioncontext
|
||||||
|
std::string s_trackPathLog;
|
||||||
|
|
||||||
|
LogMethod::
|
||||||
|
LogMethod(log4cpp::Category& log, log4cpp::Priority::Value priority,
|
||||||
|
const char *function) :
|
||||||
|
log_(log), priority_(priority), function_(function)
|
||||||
|
{
|
||||||
|
if(log_.isPriorityEnabled(priority_)) {
|
||||||
|
log_.getStream(priority_) << "Entering: " << function_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogMethod::
|
||||||
|
~LogMethod()
|
||||||
|
{
|
||||||
|
if(log_.isPriorityEnabled(priority_)) {
|
||||||
|
log_.getStream(priority_) << "Exiting: " << function_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protects against multiple calls (won't try to re-init) and gives
|
||||||
|
// back the same answer the original call got.
|
||||||
|
static int log4cppInitResult = -1;
|
||||||
|
|
||||||
|
bool
|
||||||
|
initLog4cpp(const string &confFile)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (log4cppInitResult != -1) {
|
||||||
|
return (log4cppInitResult == 0 ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
log4cppInitResult = 0; // Assume success.
|
||||||
|
try {
|
||||||
|
PropertyConfigurator::configure(confFile);
|
||||||
|
} catch (log4cpp::ConfigureFailure &e) {
|
||||||
|
cerr << "log4cpp configuration failure while loading '" <<
|
||||||
|
confFile << "' : " << e.what() << endl;
|
||||||
|
log4cppInitResult = 1;
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
cerr << "exception caught while configuring log4cpp via '" <<
|
||||||
|
confFile << "': " << e.what() << endl;
|
||||||
|
log4cppInitResult = 1;
|
||||||
|
} catch (...) {
|
||||||
|
cerr << "unknown exception while configuring log4cpp via '" <<
|
||||||
|
confFile << "'." << endl;
|
||||||
|
log4cppInitResult = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (log4cppInitResult == 0 ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For customized vim control
|
||||||
|
* Local variables:
|
||||||
|
* tab-width: 4
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* End:
|
||||||
|
* vim600: sw=4:ts=4:et
|
||||||
|
* vim<600: sw=4:ts=4:et
|
||||||
|
*/
|
130
LogUtils.h
Normal file
130
LogUtils.h
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/* Copyright (C) 2007 Yahoo! Inc. All Rights Reserved. */
|
||||||
|
|
||||||
|
#ifndef LOG_UTIL_H
|
||||||
|
#define LOG_UTIL_H
|
||||||
|
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick and dirty link between LogUtils and ActionContext without having to
|
||||||
|
* resolve cross-inclusion issues, or force all components to start including
|
||||||
|
* ActionContext if they don't already.
|
||||||
|
*/
|
||||||
|
extern std::string s_trackPathLog;
|
||||||
|
|
||||||
|
// These macros cannot be protected by braces because of the trailing stream
|
||||||
|
// arguments that get appended. Care must taken not to use them inside if/else
|
||||||
|
// blocks that do not use curly braces.
|
||||||
|
// I.e., the following will give unexpected results:
|
||||||
|
// if(foo)
|
||||||
|
// DHT_DEBUG_STREAM() << "heyheyhey";
|
||||||
|
// else
|
||||||
|
// blah();
|
||||||
|
// The 'else' will end up applying to the 'if' within the debug macro.
|
||||||
|
// Regardless of this, our standards say to always use curly brackets
|
||||||
|
// on every block anyway, no matter what.
|
||||||
|
|
||||||
|
#define DHT_DEBUG_STREAM() if(log.isDebugEnabled()) log.debugStream() << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
#define DHT_INFO_STREAM() if(log.isInfoEnabled()) log.infoStream() << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
#define DHT_INFO_WITH_STACK_STREAM() if(log.isInfoEnabled()) log.infoStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_WARN_STREAM() if(log.isWarnEnabled()) log.warnStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_ERROR_STREAM() if(log.isErrorEnabled()) log.errorStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_CRIT_STREAM() if(log.isCritEnabled()) log.critStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_TRACE_PRIORITY log4cpp::Priority::DEBUG + 50
|
||||||
|
#define DHT_TRACE_STREAM() if (log.isPriorityEnabled(DHT_TRACE_PRIORITY)) log.getStream(DHT_TRACE_PRIORITY) << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
|
||||||
|
// Sadly, sometimes 'log' is reserved by someone else so the code needs to
|
||||||
|
// use a different name for log. In that case, it can be passed in to these.
|
||||||
|
#define DHT_DEBUG_STREAML(x_log_hdl_x) if((x_log_hdl_x).isDebugEnabled()) (x_log_hdl_x).debugStream() << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
#define DHT_INFO_STREAML(x_log_hdl_x) if((x_log_hdl_x).isInfoEnabled()) (x_log_hdl_x).infoStream() << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
#define DHT_INFO_WITH_STACK_STREAML(x_log_hdl_x) if((x_log_hdl_x).isInfoEnabled()) (x_log_hdl_x).infoStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_WARN_STREAML(x_log_hdl_x) if((x_log_hdl_x).isWarnEnabled()) (x_log_hdl_x).warnStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_ERROR_STREAML(x_log_hdl_x) if((x_log_hdl_x).isErrorEnabled()) (x_log_hdl_x).errorStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_CRIT_STREAML(x_log_hdl_x) if((x_log_hdl_x).isCritEnabled()) (x_log_hdl_x).critStream() << __FUNCTION__ << "():" << __LINE__ << ":" << s_trackPathLog
|
||||||
|
#define DHT_TRACE_STREAML(x_log_hdl_x) if ((x_log_hdl_x).isPriorityEnabled(DHT_TRACE_PRIORITY)) (x_log_hdl_x).getStream(DHT_TRACE_PRIORITY) << __FUNCTION__ << "():" << __LINE__ << ":"
|
||||||
|
|
||||||
|
//Macros to use when a function returns on error without writing any log message
|
||||||
|
// or error translation
|
||||||
|
#define RETURN_IF_NOT_OK(x_call_x) \
|
||||||
|
{ \
|
||||||
|
FwCode::ResponseCode rcx___ = (x_call_x); \
|
||||||
|
if(rcx___ != FwCode::FwOk) { \
|
||||||
|
return rcx___; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RETURN_THIS_IF_NOT_OK(x_othercode_x, x_call_x) \
|
||||||
|
{ \
|
||||||
|
FwCode::ResponseCode rcx___ = (x_call_x); \
|
||||||
|
if(rcx___ != FwCode::FwOk) { \
|
||||||
|
return (x_othercode_x); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Caution! Only use in checks for 'impossible' code conditions. Regular errors
|
||||||
|
/// should be handled regularly
|
||||||
|
#define BAD_CODE_ABORT() \
|
||||||
|
{ \
|
||||||
|
std::string x_msg_x("Bad code at " __FILE__ ":"); \
|
||||||
|
x_msg_x.append(StringUtils::toString(__LINE__)); \
|
||||||
|
throw std::runtime_error(x_msg_x); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BAD_CODE_IF_NOT_OK(x_call_x) \
|
||||||
|
do {\
|
||||||
|
if((x_call_x) != FwCode::FwOk) { \
|
||||||
|
BAD_CODE_ABORT(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Above macros are meant to be used by all components.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that allows for method entry/exit logging with a single declaration.
|
||||||
|
* Always uses debug.
|
||||||
|
*/
|
||||||
|
class LogMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LogMethod(log4cpp::Category& log, log4cpp::Priority::Value priority,
|
||||||
|
const char *function);
|
||||||
|
virtual ~LogMethod();
|
||||||
|
|
||||||
|
private:
|
||||||
|
log4cpp::Category& log_;
|
||||||
|
log4cpp::Priority::Value priority_;
|
||||||
|
const char *function_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// convenience macros to use the above class
|
||||||
|
#define LOG_METHOD() LogMethod log_method_entry_exit(log, log4cpp::Priority::DEBUG, __FUNCTION__)
|
||||||
|
#define TRACE_METHOD() LogMethod log_method_entry_exit(log, DHT_TRACE_PRIORITY, __FUNCTION__)
|
||||||
|
|
||||||
|
/** Initialize log4cpp config file.
|
||||||
|
* This function needs to be called once for each executable. Multiple
|
||||||
|
* initializations will return the result of the first initialization (IOW,
|
||||||
|
* an executable can be initialized with exactly one config file). Errors
|
||||||
|
* encountered by this function are printed onto cerr. See log4cpp
|
||||||
|
* documentation for what happens when PropertyConfigurator::configure()
|
||||||
|
* fails.
|
||||||
|
* \param confFile is the path name of the log4cpp config file.
|
||||||
|
* Depending on the machine that the executable is running in, the path
|
||||||
|
* will be different.
|
||||||
|
* \return true if the initialization succeeds, false if it fails.
|
||||||
|
*/
|
||||||
|
bool initLog4cpp(const std::string & confFile);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For customized vim control
|
||||||
|
* Local variables:
|
||||||
|
* tab-width: 4
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* End:
|
||||||
|
* vim600: sw=4:ts=4:et
|
||||||
|
* vim<600: sw=4:ts=4:et
|
||||||
|
*/
|
74
Makefile
Normal file
74
Makefile
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
STASIS_DIR=../stasis
|
||||||
|
|
||||||
|
LIB=$(STASIS_DIR)/build/src/stasis \
|
||||||
|
-L/home/y/lib
|
||||||
|
INCLUDE=-I$(STASIS_DIR)/src/ -I$(STASIS_DIR) -I./ \
|
||||||
|
-I/home/y/include
|
||||||
|
|
||||||
|
LIBLIST=-lpthread \
|
||||||
|
-lstasis \
|
||||||
|
-lm
|
||||||
|
# -licui18n \
|
||||||
|
# -licuuc \
|
||||||
|
# -licudata \
|
||||||
|
# -licuio \
|
||||||
|
# -llog4cpp_y \
|
||||||
|
# -lthoth
|
||||||
|
|
||||||
|
FLAGS=-pg -g -O1
|
||||||
|
#FLAGS=-O3
|
||||||
|
|
||||||
|
HFILES=logserver.h logstore.h logiterators.h datapage.h merger.h tuplemerger.h datatuple.h
|
||||||
|
CFILES=logserver.cpp logstore.cpp logiterators.cpp datapage.cpp merger.cpp tuplemerger.cpp
|
||||||
|
|
||||||
|
|
||||||
|
# STASIS_DIR=../stasis
|
||||||
|
# LD_LIBRARY_PATH=$STASIS_DIR/build/src/stasis
|
||||||
|
# LD_LIBRARY_PATH=$STASIS_DIR/build/src/stasis ./hello
|
||||||
|
|
||||||
|
|
||||||
|
logstore: check_gen.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
test: dp_check lt_check ltable_check merger_check rb_check \
|
||||||
|
lmerger_check tmerger_check server_check tcpclient_check
|
||||||
|
|
||||||
|
lt_check: check_logtree.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
dp_check: check_datapage.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
ltable_check: check_logtable.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
merger_check: check_merge.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
lmerger_check: check_mergelarge.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
tmerger_check: check_mergetuple.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
rb_check: check_rbtree.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
server_check: check_server.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
tcpclient_check: check_tcpclient.cpp $(HFILES) $(CFILES)
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
hello : hello.cpp UCharUtils.cc LogUtils.cc
|
||||||
|
g++ -o $@ $^ -L$(LIB) $(INCLUDE) $(LIBLIST) $(FLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f logstore server_check hello lt_check merger_check lmerger_check rb_check \
|
||||||
|
dp_check ltable_check tmerger_check rose tcpclient_check
|
||||||
|
veryclean: clean
|
||||||
|
rm -f *~ gmon.out prof.res
|
||||||
|
|
||||||
|
|
||||||
|
|
152
NOTES
Normal file
152
NOTES
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
######################################################################################
|
||||||
|
constants.h
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define BLOB_THRESHOLD_SIZE (PAGE_SIZE-30)
|
||||||
|
|
||||||
|
SLOT TYPES
|
||||||
|
|
||||||
|
#define INVALID_SLOT (-1)
|
||||||
|
/** This constant is used as a placeholder to mark slot locations that contain blobs.
|
||||||
|
@see slotted.c, indirect.c, blobManager.c */
|
||||||
|
#define BLOB_SLOT (-2)
|
||||||
|
#define NORMAL_SLOT (-3)
|
||||||
|
#define SLOT_TYPE_END (-4)
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
allocationPolicy.h
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
struct allocationPolicy {
|
||||||
|
struct LH_ENTRY(table) * xidAlloced;
|
||||||
|
struct LH_ENTRY(table) * xidDealloced;
|
||||||
|
struct RB_ENTRY(tree) * availablePages;
|
||||||
|
struct LH_ENTRY(table) * pageOwners;
|
||||||
|
struct LH_ENTRY(table) * allPages;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct allocationPolicy stasis_allocation_policy_t;
|
||||||
|
|
||||||
|
typedef struct availablePage {
|
||||||
|
int freespace;
|
||||||
|
pageid_t pageid;
|
||||||
|
int lockCount; // Number of active transactions that have alloced or dealloced from this page.
|
||||||
|
} availablePage;
|
||||||
|
|
||||||
|
availablePage * stasis_allocation_policy_pick_suitable_page(stasis_allocation_policy_t * ap, int xid, int freespace);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
==15277== Thread 4:
|
||||||
|
==15277== Invalid free() / delete / delete[]
|
||||||
|
==15277== at 0x401BEFA: free (vg_replace_malloc.c:235)
|
||||||
|
==15277== by 0x4FD60FB: free_mem (in /lib/tls/libc-2.3.4.so)
|
||||||
|
==15277== by 0x4FD5B21: __libc_freeres (in /lib/tls/libc-2.3.4.so)
|
||||||
|
==15277== by 0x4017336: _vgw_freeres (vg_preloaded.c:62)
|
||||||
|
==15277== by 0x4030B25: pthread_cond_wait@@GLIBC_2.3.2 (in /lib/tls/libpthread-2.3.4.so)
|
||||||
|
==15277== by 0x402E370: start_thread (in /lib/tls/libpthread-2.3.4.so)
|
||||||
|
==15277== by 0x4F96FFD: clone (in /lib/tls/libc-2.3.4.so)
|
||||||
|
==15277== Address 0x4EC66B8 is not stack'd, malloc'd or (recently) free'd
|
||||||
|
==15277==
|
||||||
|
==15277== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 40 from 1)
|
||||||
|
==15277== malloc/free: in use at exit: 8,540,389 bytes in 912 blocks.
|
||||||
|
==15277== malloc/free: 1,815,016 allocs, 1,814,105 frees, 1,121,769,405 bytes allocated.
|
||||||
|
==15277== For counts of detected errors, rerun with: -v
|
||||||
|
==15277== searching for pointers to 912 not-freed blocks.
|
||||||
|
==15277== checked 43,383,184 bytes.
|
||||||
|
==15277==
|
||||||
|
==15277== Thread 1:
|
||||||
|
==15277==
|
||||||
|
==15277== 4,883,561 (32 direct, 4,883,529 indirect) bytes in 1 blocks are definitely lost in loss record 16 of 46
|
||||||
|
==15277== at 0x401B790: operator new(unsigned) (vg_replace_malloc.c:164)
|
||||||
|
==15277== by 0x8052C01: __gnu_cxx::new_allocator<std::_Rb_tree_node<datatuple> >::allocate(unsigned, void const*) (new_allocator.h:81)
|
||||||
|
==15277== by 0x8052B79: std::_Rb_tree<datatuple, datatuple, std::_Identity<datatuple>, datatuple, std::allocator<datatuple> >::_M_get_node() (stl_tree.h:356)
|
||||||
|
==15277== by 0x8052ACC: std::_Rb_tree<datatuple, datatuple, std::_Identity<datatuple>, datatuple, std::allocator<datatuple> >::_M_create_node(datatuple const&) (stl_tree.h:365)
|
||||||
|
==15277== by 0x8052978: std::_Rb_tree<datatuple, datatuple, std::_Identity<datatuple>, datatuple, std::allocator<datatuple> >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, datatuple const&) (stl_tree.h:783)
|
||||||
|
==15277== by 0x805270C: std::_Rb_tree<datatuple, datatuple, std::_Identity<datatuple>, datatuple, std::allocator<datatuple> >::insert_unique(datatuple const&) (stl_tree.h:881)
|
||||||
|
==15277== by 0x8052332: std::set<datatuple, datatuple, std::allocator<datatuple> >::insert(datatuple const&) (stl_set.h:314)
|
||||||
|
==15277== by 0x8050077: logtable::insertTuple(datatuple&) (logstore.cpp:1030)
|
||||||
|
==15277== by 0x804A641: insertProbeIter(int) (check_merge.cpp:160)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 336 (28 direct, 308 indirect) bytes in 1 blocks are definitely lost in loss record 17 of 46
|
||||||
|
==15277== at 0x401B405: malloc (vg_replace_malloc.c:149)
|
||||||
|
==15277== by 0x404D906: stasis_dirty_page_table_init (dirtyPageTable.c:133)
|
||||||
|
==15277== by 0x404BFA5: Tinit (transactional2.c:66)
|
||||||
|
==15277== by 0x804A2AE: insertProbeIter(int) (check_merge.cpp:97)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 40 bytes in 1 blocks are definitely lost in loss record 20 of 46
|
||||||
|
==15277== at 0x401B790: operator new(unsigned) (vg_replace_malloc.c:164)
|
||||||
|
==15277== by 0x8053025: merge_scheduler::addlogtable(logtable*) (merger.cpp:20)
|
||||||
|
==15277== by 0x804A33E: insertProbeIter(int) (check_merge.cpp:113)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 80 bytes in 10 blocks are definitely lost in loss record 32 of 46
|
||||||
|
==15277== at 0x401B405: malloc (vg_replace_malloc.c:149)
|
||||||
|
==15277== by 0x804D75E: logtree::create(int) (logstore.cpp:169)
|
||||||
|
==15277== by 0x8053BD5: memMergeThread(void*) (merger.cpp:236)
|
||||||
|
==15277== by 0x402E370: start_thread (in /lib/tls/libpthread-2.3.4.so)
|
||||||
|
==15277== by 0x4F96FFD: clone (in /lib/tls/libc-2.3.4.so)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 4,792 (432 direct, 4,360 indirect) bytes in 18 blocks are definitely lost in loss record 40 of 46
|
||||||
|
==15277== at 0x401B790: operator new(unsigned) (vg_replace_malloc.c:164)
|
||||||
|
==15277== by 0x80501C5: logtable::insertTuple(int, datatuple&, recordid&, logtree*) (logstore.cpp:1064)
|
||||||
|
==15277== by 0x8054FA7: insertTuple(int, DataPage<datatuple>*, datatuple&, logtable*, logtree*, recordid&, int&, int&) (merger.cpp:643)
|
||||||
|
==15277== by 0x8054AFF: merge_iterators(int, treeIterator<datatuple>*, memTreeIterator<std::set<datatuple, datatuple, std::allocator<datatuple> >, datatuple>*, logtable*, logtree*, int&) (merger.cpp:534)
|
||||||
|
==15277== by 0x8053C8F: memMergeThread(void*) (merger.cpp:251)
|
||||||
|
==15277== by 0x402E370: start_thread (in /lib/tls/libpthread-2.3.4.so)
|
||||||
|
==15277== by 0x4F96FFD: clone (in /lib/tls/libc-2.3.4.so)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 576 bytes in 4 blocks are possibly lost in loss record 41 of 46
|
||||||
|
==15277== at 0x401C6BF: calloc (vg_replace_malloc.c:279)
|
||||||
|
==15277== by 0x400E71A: _dl_allocate_tls (in /lib/ld-2.3.4.so)
|
||||||
|
==15277== by 0x402E91E: pthread_create@@GLIBC_2.1 (in /lib/tls/libpthread-2.3.4.so)
|
||||||
|
==15277== by 0x80538FF: merge_scheduler::startlogtable(int) (merger.cpp:184)
|
||||||
|
==15277== by 0x804A37E: insertProbeIter(int) (check_merge.cpp:116)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 3,175 bytes in 1 blocks are possibly lost in loss record 42 of 46
|
||||||
|
==15277== at 0x401B405: malloc (vg_replace_malloc.c:149)
|
||||||
|
==15277== by 0x8051BC7: DataPage<datatuple>::readbytes(int, int, int, unsigned char**) (datapage.cpp:235)
|
||||||
|
==15277== by 0x8051F7F: DataPage<datatuple>::RecordIterator::getnext(int) (datapage.cpp:442)
|
||||||
|
==15277== by 0x80512E0: DataPage<datatuple>::recordRead(int, unsigned char*, unsigned, datatuple**) (datapage.cpp:206)
|
||||||
|
==15277== by 0x8050449: logtable::findTuple(int, unsigned char*, unsigned, logtree*) (logstore.cpp:1104)
|
||||||
|
==15277== by 0x804FF48: logtable::findTuple(int, unsigned char*, unsigned) (logstore.cpp:979)
|
||||||
|
==15277== by 0x804A8D3: insertProbeIter(int) (check_merge.cpp:198)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 173,599 bytes in 2 blocks are possibly lost in loss record 43 of 46
|
||||||
|
==15277== at 0x401B405: malloc (vg_replace_malloc.c:149)
|
||||||
|
==15277== by 0x804FFD0: logtable::insertTuple(datatuple&) (logstore.cpp:1014)
|
||||||
|
==15277== by 0x804A641: insertProbeIter(int) (check_merge.cpp:160)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277==
|
||||||
|
==15277== 2,281,057 bytes in 681 blocks are definitely lost in loss record 45 of 46
|
||||||
|
==15277== at 0x401B405: malloc (vg_replace_malloc.c:149)
|
||||||
|
==15277== by 0x8051BC7: DataPage<datatuple>::readbytes(int, int, int, unsigned char**) (datapage.cpp:235)
|
||||||
|
==15277== by 0x8051F7F: DataPage<datatuple>::RecordIterator::getnext(int) (datapage.cpp:442)
|
||||||
|
==15277== by 0x80512E0: DataPage<datatuple>::recordRead(int, unsigned char*, unsigned, datatuple**) (datapage.cpp:206)
|
||||||
|
==15277== by 0x8050449: logtable::findTuple(int, unsigned char*, unsigned, logtree*) (logstore.cpp:1104)
|
||||||
|
==15277== by 0x804FF81: logtable::findTuple(int, unsigned char*, unsigned) (logstore.cpp:990)
|
||||||
|
==15277== by 0x804A8D3: insertProbeIter(int) (check_merge.cpp:198)
|
||||||
|
==15277== by 0x804AB9B: main (check_merge.cpp:235)
|
||||||
|
==15277==
|
||||||
|
==15277== LEAK SUMMARY:
|
||||||
|
==15277== definitely lost: 2,281,669 bytes in 712 blocks.
|
||||||
|
==15277== indirectly lost: 4,888,197 bytes in 150 blocks.
|
||||||
|
==15277== possibly lost: 177,350 bytes in 7 blocks.
|
||||||
|
==15277== still reachable: 1,193,173 bytes in 43 blocks.
|
||||||
|
==15277== suppressed: 0 bytes in 0 blocks.
|
||||||
|
==15277== Reachable blocks (those to which a pointer was found) are not shown.
|
||||||
|
==15277== To see them, rerun with: --show-reachable=yes
|
||||||
|
Killed
|
345
StringUtils.h
Normal file
345
StringUtils.h
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
/* $Id: StringUtils.h,v 1.17 2009/03/25 20:32:51 dlomax Exp $ */
|
||||||
|
/* Copyright (C) 2008 Yahoo! Inc. All Rights Reserved. */
|
||||||
|
|
||||||
|
#ifndef __STRING_UTIL_H
|
||||||
|
#define __STRING_UTIL_H
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include "FwCode.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for static string manipulation utilities.
|
||||||
|
*/
|
||||||
|
class StringUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our replacement for yax_getroot(). Allows our code to have a different
|
||||||
|
* root than components we use or link with. Is nice for unit testing.
|
||||||
|
* @return Copy of the value in a std::string
|
||||||
|
*/
|
||||||
|
static std::string getDhtRoot();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a tablet name into left and right limits.
|
||||||
|
* @return true if parsing successful, false if incorrect format
|
||||||
|
*/
|
||||||
|
static bool parseTabletName(const std::string& tablet, std::string& leftLimit,
|
||||||
|
std::string& rightLimit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a tablet name from left and right limits.
|
||||||
|
*/
|
||||||
|
static void buildTabletName(const std::string& leftLimit,
|
||||||
|
const std::string& rightLimit,
|
||||||
|
std::string& tablet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General purpose method to assemble a full path name, using
|
||||||
|
* getDhtRoot() so that
|
||||||
|
* the root will be configurable. DO NOT supply "/home/y" in path1.
|
||||||
|
*/
|
||||||
|
static std::string makePath(const std::string& path1 = "",
|
||||||
|
const std::string& path2 = "",
|
||||||
|
const std::string& path3 = "",
|
||||||
|
const std::string& path4 = "",
|
||||||
|
const std::string& path5 = "",
|
||||||
|
const std::string& path6 = "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append additional paths to an existing one - does not prepend ROOT.
|
||||||
|
*/
|
||||||
|
static void appendPath(std::string& base_path, const std::string& path2 = "",
|
||||||
|
const std::string& path3 = "",
|
||||||
|
const std::string& path4 = "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a topic name from a table/tablet.
|
||||||
|
*
|
||||||
|
* @return the topic name
|
||||||
|
*/
|
||||||
|
static std::string buildTopicName(const std::string& table,
|
||||||
|
const std::string& tablet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a topic name from a table/tablet.
|
||||||
|
* @param topic Is filled with the topic name.
|
||||||
|
*/
|
||||||
|
static void buildTopicName(const std::string& table,
|
||||||
|
const std::string& tablet,
|
||||||
|
std::string &topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses <code>topic</code> into table and tablet portions.
|
||||||
|
*
|
||||||
|
* @param table Filled with the table name.
|
||||||
|
* @param tablet Filled with the tablet name.
|
||||||
|
* @param true if the parsing succeeded, false if not.
|
||||||
|
*/
|
||||||
|
static bool parseTopicName(const std::string& topic,
|
||||||
|
std::string& table,
|
||||||
|
std::string &tablet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for use in log statements - this is slow. Produce a printable
|
||||||
|
* string where binary (<32) characters are hex encoded, but all others
|
||||||
|
* are left alone.
|
||||||
|
*
|
||||||
|
* @param str string to encode
|
||||||
|
* @param len length of string
|
||||||
|
* @return encoded string.
|
||||||
|
*/
|
||||||
|
static std::string toPrintable(const char *str, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a formatted hex string back into its original
|
||||||
|
* 64-bit value
|
||||||
|
*
|
||||||
|
* @param value the hex-encoded string
|
||||||
|
* @param out the value
|
||||||
|
* @return FwCode::FwOk on success, FwCode::BadHexString on parse failure
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode
|
||||||
|
convertHexStringToUI64(const std::string& value, uint64_t& out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a formatted hex string back into its original
|
||||||
|
* 32-bit value
|
||||||
|
*
|
||||||
|
* @param value the hex-encoded string
|
||||||
|
* @param out the value
|
||||||
|
* @return FwCode::FwOk on success, FwCode::BadHexString on parse failure
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode
|
||||||
|
convertHexStringToUI32(const std::string& value, uint32_t& out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard means for formatting a 0x prefixed hex string from a
|
||||||
|
* 64-bit unsigned value. Will produce upper-case letters. Will
|
||||||
|
* pad with zeros at the beginning to fill out 16 hex chars.
|
||||||
|
*
|
||||||
|
* @param the value to format
|
||||||
|
* @return the formatted value, like "0xDEADBEEF00000000"
|
||||||
|
*/
|
||||||
|
static std::string convertUI64ToHexString( uint64_t val );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard means for formatting a 0x prefixed hex string from a
|
||||||
|
* 32-bit unsigned value. Will produce upper-case letters. Will
|
||||||
|
* pad with zeros at the beginning to fill out 8 hex chars.
|
||||||
|
*
|
||||||
|
* @param the value to format
|
||||||
|
* @return the formatted value, like "0xDEADBEEF"
|
||||||
|
*/
|
||||||
|
static std::string convertUI32ToHexString( unsigned int val );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard means for formatting a small hex string from a
|
||||||
|
* 32-bit unsigned value. The "0x" will NOT be included.
|
||||||
|
* Will produce upper-case letters. Will NOT pad with zeros
|
||||||
|
* at the beginning.
|
||||||
|
*
|
||||||
|
* @param the value to format
|
||||||
|
* @return the formatted value, like "DEADBEEF"
|
||||||
|
*/
|
||||||
|
static std::string convertUI32ToMinimalHexString( unsigned int val );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble the fields of ENCRYPTED_BODY_HEADER and encrypt it for
|
||||||
|
* sending to the remote side.
|
||||||
|
* @param result is the out parameter having the resulting string.
|
||||||
|
* @param encKeyName is the name of the key in keydb whose value will be
|
||||||
|
* used as the encryption key
|
||||||
|
* @param bodyEncVersion is the version of the encryption scheme used to
|
||||||
|
* encrypt the body (not the encryption scheme of this header itself).
|
||||||
|
* @param expireTime is the time (in usecs) after which the request
|
||||||
|
* should not be processed by the receiver of this header.
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode makeEncryptedBodyHdr(std::string & result,
|
||||||
|
const char *encKeyName, uint32_t bodyEncVersion, uint64_t expireTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the incoming ENCRYPTED_BODY_HEADER, decrypting it, and
|
||||||
|
* separating the fields in it.
|
||||||
|
* @param inval is the incoming encrypted string.
|
||||||
|
* @param encKeyName is the name of the key in keydb whose value will be
|
||||||
|
* used as the decryption key
|
||||||
|
* @param bodyEncVersion is the version of the encryption scheme to be
|
||||||
|
* used to * decrypt the body (not for the decryption of this header
|
||||||
|
* itself).
|
||||||
|
* @param expireTime is the time (in usecs) after which the response
|
||||||
|
* should not be processed by the receiver of this header.
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode parseEncryptedBodyHdr(const std::string & inval,
|
||||||
|
const char *encKeyName, uint32_t & bodyEncVersion, uint64_t & expireTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash for an un-normalized record name.
|
||||||
|
*
|
||||||
|
* @param unnormalizedRecordName a raw record name from user input
|
||||||
|
* @param (output) hashResult the hex string of the hash value.
|
||||||
|
* @return FwCode::FwOk on success, else an error relating to normalization
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode normalizeAndHashRecordName
|
||||||
|
( const std::string& unnormalizedRecordName,
|
||||||
|
std::string & hashResult /* out */ );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash for a normalized record name.
|
||||||
|
*
|
||||||
|
* @param recordName the record name. MUST be previously normalized.
|
||||||
|
* @return hashResult the uint32_t of the hash value.
|
||||||
|
*/
|
||||||
|
static uint32_t hashRecordName(const std::string& recordName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash for a normalized record name.
|
||||||
|
*
|
||||||
|
* @param recordName the record name. MUST be previously normalized.
|
||||||
|
* @param (output) hashResult the hex string of the hash value.
|
||||||
|
*/
|
||||||
|
static void hashRecordName( const std::string& recordName,
|
||||||
|
std::string & hashResult /* out */ );
|
||||||
|
/**
|
||||||
|
* Get the hash for a normalized record name in string and int form
|
||||||
|
*
|
||||||
|
* @param recordName the record name. MUST be previously normalized.
|
||||||
|
* @param (output) hashResult the hex string of the hash value.
|
||||||
|
* @param (output) hexNum numerical value of hash
|
||||||
|
*/
|
||||||
|
static void hashRecordName( const std::string& recordName,
|
||||||
|
std::string & hashResult /* out */,
|
||||||
|
uint32_t& hexNum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to hash a string using crc32.
|
||||||
|
*
|
||||||
|
* @param buf data to hash
|
||||||
|
* @param len length of buf
|
||||||
|
* @return hash value
|
||||||
|
*/
|
||||||
|
static uint32_t crcHash(const char * buf, uint32_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* util function to convert any type to a string
|
||||||
|
*/
|
||||||
|
template<typename T> static inline std::string toString(T item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert string to any type of value
|
||||||
|
* @param strValue string value to parse
|
||||||
|
* @param value(out) value to read from strValue
|
||||||
|
* @return FwCode::FwOk on success
|
||||||
|
* FwCode::FwError on failure (error is *not* logged)
|
||||||
|
*/
|
||||||
|
template<typename T> static inline
|
||||||
|
FwCode::ResponseCode fromString(const std::string& strValue,
|
||||||
|
T& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert a hexadecimal number to string representation
|
||||||
|
* of fixed width ( 2 * sizeof(T) )
|
||||||
|
* @param value number to convert to string
|
||||||
|
* @return string representation of value
|
||||||
|
*/
|
||||||
|
template<typename T> static inline
|
||||||
|
std::string numberToHexString(T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert a hexadecimal number to minimal string representation
|
||||||
|
* @param value number to convert to string
|
||||||
|
* @return string representation of value
|
||||||
|
*/
|
||||||
|
template<typename T> static inline
|
||||||
|
std::string numberToMinimalHexString(T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert a hexadecimal string to a number
|
||||||
|
* @param strvalue input string to read from
|
||||||
|
* @param value(out) output number
|
||||||
|
* @return FwCode::FwOk on successful conversion
|
||||||
|
* FwCode::FwError on failure to convert strvalue
|
||||||
|
* to number
|
||||||
|
*/
|
||||||
|
template<typename T> static inline
|
||||||
|
FwCode::ResponseCode hexStringToNumber(const std::string& strvalue,
|
||||||
|
T& value);
|
||||||
|
|
||||||
|
|
||||||
|
static const std::string EMPTY_STRING;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::string StringUtils::
|
||||||
|
toString(T item)
|
||||||
|
{
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << item;
|
||||||
|
return buf.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
FwCode::ResponseCode StringUtils::
|
||||||
|
fromString(const std::string& strValue,
|
||||||
|
T& value)
|
||||||
|
{
|
||||||
|
std::istringstream buf(strValue);
|
||||||
|
buf >> value;
|
||||||
|
if(buf.fail()||
|
||||||
|
(strValue.length() != buf.tellg() ))
|
||||||
|
{
|
||||||
|
return FwCode::FwError;
|
||||||
|
}
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::string StringUtils::
|
||||||
|
numberToHexString(T value)
|
||||||
|
{
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << "0x" << std::hex
|
||||||
|
<< std::setw(sizeof(T) * 2) << std::setfill('0')
|
||||||
|
<< std::uppercase << value;
|
||||||
|
return buf.str();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::string StringUtils::
|
||||||
|
numberToMinimalHexString(T value)
|
||||||
|
{
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << std::hex << std::uppercase << value;
|
||||||
|
return buf.str();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
FwCode::ResponseCode StringUtils::
|
||||||
|
hexStringToNumber(const std::string& strvalue,
|
||||||
|
T& value)
|
||||||
|
{
|
||||||
|
std::istringstream buf(strvalue);
|
||||||
|
buf >> std::hex >> value;
|
||||||
|
if(buf.fail() ||
|
||||||
|
(strvalue.length() != buf.tellg() ))
|
||||||
|
{
|
||||||
|
return FwCode::FwError;
|
||||||
|
}
|
||||||
|
return FwCode::FwOk;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For customized vim control
|
||||||
|
* Local variables:
|
||||||
|
* tab-width: 4
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* End:
|
||||||
|
* vim600: sw=4:ts=4:et
|
||||||
|
* vim<600: sw=4:ts=4:et
|
||||||
|
*/
|
||||||
|
#endif
|
326
UCharUtils.cc
Normal file
326
UCharUtils.cc
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
/* $Id: UCharUtils.cc,v 1.16 2009/03/03 20:19:18 dlomax Exp $ */
|
||||||
|
/* Copyright (C) 2008 Yahoo! Inc. All Rights Reserved. */
|
||||||
|
|
||||||
|
//#include <dht/UCharUtils.h>
|
||||||
|
#include "UCharUtils.h"
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
#include "LogUtils.h"
|
||||||
|
//#include "ActionContext.h"
|
||||||
|
#include <unicode/ucnv.h>
|
||||||
|
#include <unicode/unorm.h>
|
||||||
|
#include <thoth/validate.h> // To make sure we have UTF-8
|
||||||
|
|
||||||
|
static log4cpp::Category &log =
|
||||||
|
log4cpp::Category::getInstance("dht.framework." __FILE__);
|
||||||
|
|
||||||
|
|
||||||
|
UCharUtilsImpl *UCharUtils::instance_ = NULL;
|
||||||
|
|
||||||
|
UCharUtilsImpl::
|
||||||
|
UCharUtilsImpl() : uconv_(NULL) {
|
||||||
|
LOG_METHOD();
|
||||||
|
|
||||||
|
ucBuffLen = 0;
|
||||||
|
ucBuff = NULL;
|
||||||
|
|
||||||
|
ucNormBuffLen = 0;
|
||||||
|
ucNormBuff = NULL;
|
||||||
|
|
||||||
|
charBuffLen = 0;
|
||||||
|
charBuff = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FwCode::ResponseCode UCharUtilsImpl::
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
UErrorCode erc = U_ZERO_ERROR;
|
||||||
|
|
||||||
|
uconv_ = ucnv_open("utf-8", &erc);
|
||||||
|
if (uconv_ == NULL) {
|
||||||
|
DHT_ERROR_STREAM() << "EC:UNICODE:Problem geting utf-8 converter, erc:" << erc
|
||||||
|
<< ", " << u_errorName(erc);
|
||||||
|
return FwCode::UcnvOpenFailed;
|
||||||
|
}
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCharUtilsImpl::
|
||||||
|
~UCharUtilsImpl() {
|
||||||
|
reset();
|
||||||
|
if (uconv_ != NULL) {
|
||||||
|
ucnv_close(uconv_);
|
||||||
|
uconv_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCharUtilsImpl::
|
||||||
|
reset() {
|
||||||
|
LOG_METHOD();
|
||||||
|
|
||||||
|
if (ucBuff != NULL) {
|
||||||
|
delete[] ucBuff;
|
||||||
|
ucBuffLen = 0;
|
||||||
|
ucBuff = NULL;
|
||||||
|
}
|
||||||
|
if (ucNormBuff != NULL) {
|
||||||
|
delete[] ucNormBuff;
|
||||||
|
ucNormBuffLen = 0;
|
||||||
|
ucNormBuff = NULL;
|
||||||
|
}
|
||||||
|
if (charBuff != NULL) {
|
||||||
|
delete[] charBuff;
|
||||||
|
charBuffLen = 0;
|
||||||
|
charBuff = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small wrapper to hide multi-line thoth api inside single-line call.
|
||||||
|
*/
|
||||||
|
bool UCharUtils::
|
||||||
|
isUTF8(const std::string& value)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
thoth_result result = thoth_validate_utf8(value.c_str(), value.length(),
|
||||||
|
&pos);
|
||||||
|
|
||||||
|
if(result != UTF8_VALID) {
|
||||||
|
std::cerr
|
||||||
|
//RESPONSE_DEBUG_STREAM(FwCode::DataNotUtf8)
|
||||||
|
<< "value (" << value << ") is not UTF-8. thoth_result:" << result
|
||||||
|
<< ", position=" << pos;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small wrapper to hide multi-line thoth api inside single-line call.
|
||||||
|
*/
|
||||||
|
bool UCharUtils::
|
||||||
|
isUTF8(const char * value, size_t value_len)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
thoth_result result = thoth_validate_utf8(value, value_len, &pos);
|
||||||
|
|
||||||
|
if(result != UTF8_VALID) {
|
||||||
|
//RESPONSE_DEBUG_STREAM(FwCode::DataNotUtf8)
|
||||||
|
std::cerr
|
||||||
|
<< "value (" << std::string(value, value_len)
|
||||||
|
<< ") is not UTF-8. thoth_result:" << result
|
||||||
|
<< ", position=" << pos;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an input string (expected to be UTF-8) into unicode UChars
|
||||||
|
// The result of the conversion will be sitting in our ucBuff area.
|
||||||
|
FwCode::ResponseCode UCharUtilsImpl::
|
||||||
|
convert(const std::string &input, int32_t &len)
|
||||||
|
{
|
||||||
|
LOG_METHOD();
|
||||||
|
|
||||||
|
//UTF-8 validation
|
||||||
|
if(!UCharUtils::isUTF8(input)) {
|
||||||
|
return FwCode::DataNotUtf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = input.length() * 2;
|
||||||
|
|
||||||
|
// Check if we already have a big enough buffer
|
||||||
|
if (ucBuffLen < size) {
|
||||||
|
// Nope, first check if we need to release what we've been using
|
||||||
|
if (ucBuff) {
|
||||||
|
delete[] ucBuff;
|
||||||
|
}
|
||||||
|
ucBuffLen = size;
|
||||||
|
ucBuff = new UChar[ucBuffLen];
|
||||||
|
}
|
||||||
|
|
||||||
|
UErrorCode erc = U_ZERO_ERROR;
|
||||||
|
len = ucnv_toUChars(uconv_,
|
||||||
|
ucBuff,
|
||||||
|
ucBuffLen,
|
||||||
|
input.data(),
|
||||||
|
input.length(), &erc);
|
||||||
|
|
||||||
|
if (U_FAILURE(erc)) {
|
||||||
|
//RESPONSE_ERROR_STREAM(FwCode::ConvertToUCharFailed)
|
||||||
|
std::cerr
|
||||||
|
<< "EC:UNICODE:error:" << erc
|
||||||
|
<< ", " << u_errorName(erc)
|
||||||
|
<< " from converting input:'" << input << "'";
|
||||||
|
len = 0;
|
||||||
|
return FwCode::ConvertToUCharFailed;
|
||||||
|
}
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize an input string. Note that all three internal buffers will
|
||||||
|
// be used by this operation, but by the time we finish, we'll be done
|
||||||
|
// with them.
|
||||||
|
FwCode::ResponseCode UCharUtilsImpl::
|
||||||
|
normalize(const std::string &input, std::string &result /* out */)
|
||||||
|
{
|
||||||
|
LOG_METHOD();
|
||||||
|
|
||||||
|
// convert our UTF-8 into UChar
|
||||||
|
int32_t inLen = 0;
|
||||||
|
FwCode::ResponseCode rc = convert(input, inLen);
|
||||||
|
|
||||||
|
if (rc != FwCode::FwOk) {
|
||||||
|
result.erase();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a quick check if the input is already normalized so that
|
||||||
|
// we can duck out early
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
if (unorm_quickCheck(ucBuff, inLen,
|
||||||
|
UNORM_NFC, &status) == UNORM_YES) {
|
||||||
|
DHT_DEBUG_STREAM() << "already normalized input:" << input;
|
||||||
|
result = input;
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough space for the normalized result.
|
||||||
|
// We'll make the output space twice as big as the input (although
|
||||||
|
// it's more likely that the normalized result will be shorter
|
||||||
|
// as it combines characters. E.g. 'A' 'put an accent on the previous'
|
||||||
|
int32_t newSize = inLen * 2;
|
||||||
|
if (newSize > ucNormBuffLen) {
|
||||||
|
DHT_DEBUG_STREAM() << "newSize:" << newSize
|
||||||
|
<< " ucNormBuffLen:" << ucNormBuffLen;
|
||||||
|
if (ucNormBuff) {
|
||||||
|
delete[] ucNormBuff;
|
||||||
|
}
|
||||||
|
ucNormBuffLen = newSize;
|
||||||
|
ucNormBuff = new UChar[ucNormBuffLen];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the actual normalization
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
int32_t normLen = unorm_normalize(ucBuff, inLen,
|
||||||
|
UNORM_NFC, 0,
|
||||||
|
ucNormBuff,
|
||||||
|
ucNormBuffLen,
|
||||||
|
&status);
|
||||||
|
if(U_FAILURE(status)) {
|
||||||
|
//RESPONSE_ERROR_STREAM(FwCode::FwError)
|
||||||
|
std::cerr
|
||||||
|
<< "EC:UNICODE:error:" << status << ", " << u_errorName(status)
|
||||||
|
<<" in unorm_normalize, inLen:" << inLen
|
||||||
|
<< " ucNormBuffLen:" << ucNormBuffLen;
|
||||||
|
return FwCode::FwError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have some space to convert back to UTF-8
|
||||||
|
int32_t resultLen = normLen * 4;
|
||||||
|
if (resultLen > charBuffLen) {
|
||||||
|
DHT_DEBUG_STREAM() << "resultLen:" << resultLen
|
||||||
|
<< " charBuffLen:" << charBuffLen;
|
||||||
|
if (charBuff) {
|
||||||
|
delete[] charBuff;
|
||||||
|
charBuff= NULL;
|
||||||
|
}
|
||||||
|
charBuffLen = resultLen;
|
||||||
|
charBuff = new char[charBuffLen];
|
||||||
|
}
|
||||||
|
|
||||||
|
DHT_DEBUG_STREAM() <<"calling ucnv_fromUChars, normLen:" << normLen;
|
||||||
|
|
||||||
|
// Go from UChar array to UTF-8
|
||||||
|
int32_t actualLen = ucnv_fromUChars(uconv_,
|
||||||
|
charBuff, charBuffLen,
|
||||||
|
ucNormBuff, normLen,
|
||||||
|
&status);
|
||||||
|
if(U_FAILURE(status)) {
|
||||||
|
//RESPONSE_ERROR_STREAM(FwCode::FwError)
|
||||||
|
std::cerr
|
||||||
|
<< "EC:UNICODE:error:" << status << ", " << u_errorName(status)
|
||||||
|
<< " in ucnv_fromUChars charBuffLen:" << charBuffLen
|
||||||
|
<< " normLen:" << normLen;
|
||||||
|
return FwCode::FwError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smack our UTF-8 characters into the result string
|
||||||
|
result.assign(charBuff, actualLen);
|
||||||
|
DHT_DEBUG_STREAM() << "leaving actualLen:" << actualLen
|
||||||
|
<< " result:" << result;
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FwCode::ResponseCode UCharUtils::
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
if (instance_ == NULL) {
|
||||||
|
instance_ = new UCharUtilsImpl();
|
||||||
|
return instance_->init();
|
||||||
|
}
|
||||||
|
return FwCode::FwOk; // already initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCharUtils::
|
||||||
|
close()
|
||||||
|
{
|
||||||
|
if(instance_ != NULL) {
|
||||||
|
delete instance_;
|
||||||
|
instance_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an input string, return a unicode UChar array. Note that the
|
||||||
|
// return value is a pointer to our internal buffer.
|
||||||
|
UChar * UCharUtils::
|
||||||
|
getUChar(const std::string &input, int32_t& len) {
|
||||||
|
LOG_METHOD();
|
||||||
|
|
||||||
|
// do the conversion...somehow need 2x input len for utf8 to utf16
|
||||||
|
if(instance_->convert(input, len) != FwCode::FwOk) {
|
||||||
|
len = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance_->ucBuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
FwCode::ResponseCode UCharUtils::
|
||||||
|
normalize(const std::string &input, std::string &result) {
|
||||||
|
LOG_METHOD();
|
||||||
|
return(instance_->normalize(input, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FwCode::ResponseCode UCharUtils::
|
||||||
|
parseRegExpPattern(const std::string &pattern,
|
||||||
|
URegularExpression * & result /* out */)
|
||||||
|
{
|
||||||
|
UParseError perr;
|
||||||
|
UErrorCode erc = U_ZERO_ERROR;
|
||||||
|
int32_t ureglen = 0;
|
||||||
|
|
||||||
|
// Do not delete uregexp, it's a static reusable buffer inside UCharUtils
|
||||||
|
UChar *uregexp = UCharUtils::getUChar(pattern, ureglen);
|
||||||
|
if (uregexp == NULL) {
|
||||||
|
//RESPONSE_ERROR_STREAM(FwCode::ConvertToUCharFailed)
|
||||||
|
std::cerr
|
||||||
|
<< "EC:UNICODE|IMPOSSIBLE:Unable to convert pattern to unicode: " << pattern;
|
||||||
|
return FwCode::ConvertToUCharFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
URegularExpression *regexp= uregex_open(uregexp, ureglen, 0,
|
||||||
|
&perr,
|
||||||
|
&erc);
|
||||||
|
if(erc != U_ZERO_ERROR) {
|
||||||
|
//RESPONSE_DEBUG_STREAM(FwCode::CompileRegExFailed)
|
||||||
|
std::cerr
|
||||||
|
<< "Compiling regex failed at: " << perr.offset
|
||||||
|
<< "; re=" << pattern;
|
||||||
|
return FwCode::CompileRegExFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = regexp;
|
||||||
|
return FwCode::FwOk;
|
||||||
|
}
|
139
UCharUtils.h
Normal file
139
UCharUtils.h
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/* Copyright (C) 2008 Yahoo! Inc. All Rights Reserved. */
|
||||||
|
|
||||||
|
#ifndef UCHAR_UTILS_H
|
||||||
|
#define UCHAR_UTILS_H
|
||||||
|
|
||||||
|
#include <unicode/ucnv.h>
|
||||||
|
#include <string>
|
||||||
|
#include "FwCode.h"
|
||||||
|
#include <unicode/uregex.h>
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class UCharUtilsImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some handy utilities for working with unicode characters. Yes, these
|
||||||
|
* could have just been some regular routines instead of static methods
|
||||||
|
* in a class, but doing it this way gives us some containment of what
|
||||||
|
* other static tidbits might be necessary (like reusable buffer space).
|
||||||
|
* which are all hidden within the UCharUtilsImpl class.
|
||||||
|
*
|
||||||
|
* This is a singleton - do not use in a threaded program.
|
||||||
|
*/
|
||||||
|
class UCharUtils {
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our pointer to all sorts of goodness.
|
||||||
|
*/
|
||||||
|
static UCharUtilsImpl *instance_;
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the utilities. Primarily opens the utf-8 converter.
|
||||||
|
* Calling this is required prior to using the converter.
|
||||||
|
*
|
||||||
|
* @return FwCode::FwOk on success, FwCode::UcnvOpenFailed on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release all resources. <code>init()</code> must be called again
|
||||||
|
* in order to use again.
|
||||||
|
*/
|
||||||
|
static void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small wrapper to hide multi-line thoth api inside single-line call.
|
||||||
|
*
|
||||||
|
* @param value string to be tested for utf-8-ness
|
||||||
|
* @return true if it is utf-8, false if not
|
||||||
|
*/
|
||||||
|
static bool isUTF8(const std::string& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small wrapper to hide multi-line thoth api inside single-line call.
|
||||||
|
*
|
||||||
|
* @param value char string to be tested for utf-8-ness
|
||||||
|
* @param value_len length of <code>value</code>
|
||||||
|
* @return true if it is utf-8, false if not
|
||||||
|
*/
|
||||||
|
static bool isUTF8(const char * value, size_t value_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert utf-8 strings into UChar strings. Note that the
|
||||||
|
* result is an internal reusable buffer so the caller should
|
||||||
|
* *not* release it.
|
||||||
|
* @param input utf-8 string to convert
|
||||||
|
* @param len set to length of output string
|
||||||
|
* @return NULL if anything bad happens, otherwise an allocated UChar *
|
||||||
|
* the caller must *NEVER* free this pointer.
|
||||||
|
*/
|
||||||
|
static UChar * getUChar(const std::string &input, int32_t& len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a NFC normalization so that different yet equivalent strings
|
||||||
|
* will have a single representation. See
|
||||||
|
* http://www.unicode.org/unicode/reports/tr15/
|
||||||
|
* for more information.
|
||||||
|
* @param input A UTF-8 string that we want to normalize
|
||||||
|
* @param result (output) the normalized UTF-8 string
|
||||||
|
* @return FwCode::FwOk on success,
|
||||||
|
* FwCode::FwError on conversion failure,
|
||||||
|
* FwCode::InvalidData if input was not utf-8
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode normalize(const std::string &input,
|
||||||
|
std::string &result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile a regular expression in a unicode-friendly way.
|
||||||
|
*
|
||||||
|
* @param pattern the regexp pattern to compile. Assumed to
|
||||||
|
* be utf-8.
|
||||||
|
* @param result (output) Set to point to the compiled regexp.
|
||||||
|
* Must be released by the caller via uregex_close() when
|
||||||
|
* finished with it.
|
||||||
|
* @return FwCode::FwOk if compilation succeeded,
|
||||||
|
* FwCode::CompileRegExFailed or FwCode::ConvertToUCharFailed
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
static FwCode::ResponseCode parseRegExpPattern
|
||||||
|
(const std::string &pattern,
|
||||||
|
URegularExpression * & result /* out */);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bug 2574599 - Impl exposed for use by multiple threads; singleton not
|
||||||
|
* appropriate for multi-threaded program.
|
||||||
|
*/
|
||||||
|
class UCharUtilsImpl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
UConverter *uconv_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UCharUtilsImpl();
|
||||||
|
~UCharUtilsImpl();
|
||||||
|
|
||||||
|
FwCode::ResponseCode init();
|
||||||
|
void reset();
|
||||||
|
FwCode::ResponseCode convert(const std::string &input, int32_t &len);
|
||||||
|
|
||||||
|
FwCode::ResponseCode normalize(const std::string &nput, std::string &result);
|
||||||
|
|
||||||
|
// Buffer used to convert from UTF-* into UChar
|
||||||
|
int32_t ucBuffLen;
|
||||||
|
UChar *ucBuff;
|
||||||
|
|
||||||
|
// Buffer used for UChar normalization output
|
||||||
|
int32_t ucNormBuffLen;
|
||||||
|
UChar *ucNormBuff;
|
||||||
|
|
||||||
|
// Buffer used to convert UChars back to UTF-8
|
||||||
|
int32_t charBuffLen;
|
||||||
|
char *charBuff;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _DHT_UCHAR_UTILS_
|
130
adriana-lima.awk
Executable file
130
adriana-lima.awk
Executable file
|
@ -0,0 +1,130 @@
|
||||||
|
#! /usr/bin/awk -f
|
||||||
|
|
||||||
|
BEGIN{
|
||||||
|
|
||||||
|
READ_SLA = 500;
|
||||||
|
WRITE_SLA = 750;
|
||||||
|
|
||||||
|
readcnt = 0;
|
||||||
|
writecnt = 0;
|
||||||
|
|
||||||
|
wlat_tot = 0;
|
||||||
|
wlat_max = 0;
|
||||||
|
wlat_sqtot = 0;
|
||||||
|
wlat_slafail = 0;
|
||||||
|
|
||||||
|
DIST_BUCKET_LENGTH = 100;
|
||||||
|
DIST_BUCKET_COUNT = 20;
|
||||||
|
for(i=1; i<=DIST_BUCKET_COUNT; i++)
|
||||||
|
{
|
||||||
|
rlat_dist[i] = 0;
|
||||||
|
wlat_dist[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rlat_tot = 0;
|
||||||
|
rlat_max = 0;
|
||||||
|
rlat_sqtot = 0;
|
||||||
|
rlat_slafail = 0;
|
||||||
|
|
||||||
|
printf("READ SLA:\t%d\n", READ_SLA);
|
||||||
|
printf("WRITE SLA:\t%d\n", WRITE_SLA);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/INFO - doRead()/ { readcnt = readcnt + 1;
|
||||||
|
|
||||||
|
split(substr($0, match($0, "latency:")+ length("latency:")+1), tmp_arr, " ");
|
||||||
|
#printf("%d\n", strtonum(tmp_arr[1]));
|
||||||
|
|
||||||
|
lat_val = strtonum(tmp_arr[1]);
|
||||||
|
|
||||||
|
dist_index = int(lat_val / DIST_BUCKET_LENGTH) + 1;
|
||||||
|
if(dist_index > DIST_BUCKET_COUNT)
|
||||||
|
dist_index = DIST_BUCKET_COUNT;
|
||||||
|
rlat_dist[dist_index]++;
|
||||||
|
|
||||||
|
rlat_tot = rlat_tot + lat_val;
|
||||||
|
|
||||||
|
rlat_sqtot = rlat_sqtot + lat_val*lat_val;
|
||||||
|
|
||||||
|
if(lat_val > rlat_max)
|
||||||
|
rlat_max = lat_val;
|
||||||
|
|
||||||
|
if(lat_val > READ_SLA)
|
||||||
|
rlat_slafail = rlat_slafail + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/INFO - doInsert()/ { writecnt = writecnt + 1;
|
||||||
|
|
||||||
|
split(substr($0, match($0, "latency:")+ length("latency:")+1), tmp_arr, " ");
|
||||||
|
|
||||||
|
lat_val = tmp_arr[1];
|
||||||
|
|
||||||
|
if(index(tmp_arr[1], ",")!= 0)
|
||||||
|
lat_val = substr(tmp_arr[1],1,index(tmp_arr[1],",")-1);
|
||||||
|
|
||||||
|
#printf("%d\n", strtonum(lat_val));
|
||||||
|
lat_val = strtonum(lat_val);
|
||||||
|
|
||||||
|
dist_index = int(lat_val / DIST_BUCKET_LENGTH) + 1;
|
||||||
|
if(dist_index > DIST_BUCKET_COUNT)
|
||||||
|
dist_index = DIST_BUCKET_COUNT;
|
||||||
|
wlat_dist[dist_index]++;
|
||||||
|
|
||||||
|
wlat_tot = wlat_tot + lat_val;
|
||||||
|
|
||||||
|
wlat_sqtot = wlat_sqtot + lat_val*lat_val;
|
||||||
|
|
||||||
|
if(lat_val > wlat_max)
|
||||||
|
wlat_max = lat_val;
|
||||||
|
|
||||||
|
if(lat_val > WRITE_SLA)
|
||||||
|
wlat_slafail = wlat_slafail + 1;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
END{
|
||||||
|
|
||||||
|
printf("R/W ratio:\t%.2f\n", strtonum(readcnt) / strtonum(writecnt));
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("#reads:\t%d\n",readcnt);
|
||||||
|
if(strtonum(readcnt) != 0)
|
||||||
|
{
|
||||||
|
printf("avg read latency:\t%.2f\n", (rlat_tot / readcnt));
|
||||||
|
printf("var read latency:\t%.2f\n", (rlat_sqtot/readcnt) - (rlat_tot/readcnt)*(rlat_tot/readcnt));
|
||||||
|
printf("max read latency:\t%.2f\n", rlat_max);
|
||||||
|
printf("read SLA fail:\t%d\n", rlat_slafail);
|
||||||
|
|
||||||
|
printf("\nREAD LATENCY DISTRIBUTION\n");
|
||||||
|
for(i=1; i<DIST_BUCKET_COUNT; i++)
|
||||||
|
printf("\t%d - %d:\t%d\n", (i-1)*DIST_BUCKET_LENGTH, i*DIST_BUCKET_LENGTH-1, rlat_dist[i]);
|
||||||
|
printf("\t%d - Inf:\t%d\n", (i-1)*DIST_BUCKET_LENGTH, rlat_dist[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("#writes:\t%d\n",writecnt);
|
||||||
|
if(strtonum(writecnt) != 0)
|
||||||
|
{
|
||||||
|
printf("avg write latency:\t%.2f\n", (wlat_tot / writecnt));
|
||||||
|
printf("var write latency:\t%.2f\n", (wlat_sqtot/writecnt) - (wlat_tot/writecnt)*(wlat_tot/writecnt));
|
||||||
|
printf("max write latency:\t%.2f\n", wlat_max);
|
||||||
|
printf("write SLA fail:\t%d\n", wlat_slafail);
|
||||||
|
|
||||||
|
printf("\nWRITE LATENCY DISTRIBUTION\n");
|
||||||
|
for(i=1; i<DIST_BUCKET_COUNT; i++)
|
||||||
|
printf("\t%d - %d:\t%d\n", (i-1)*DIST_BUCKET_LENGTH, i*DIST_BUCKET_LENGTH-1, wlat_dist[i]);
|
||||||
|
printf("\t%d - Inf:\t%d\n", (i-1)*DIST_BUCKET_LENGTH, wlat_dist[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
321
check_datapage.cpp
Normal file
321
check_datapage.cpp
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
template class DataPage<datatuple>;
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> &arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr.size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp(arr[i], arr[i-1]) || mycmp(arr[i-1], arr[i])))
|
||||||
|
arr.erase(arr.begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> &arr, int avg_len=50, bool duplicates_allowed=false)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
//make sure there is no duplicate key
|
||||||
|
if(!duplicates_allowed)
|
||||||
|
{
|
||||||
|
bool dup = false;
|
||||||
|
for(int i=0; i<j; i++)
|
||||||
|
if(! (mycmp(arr[i], str) || mycmp(str, arr[i])))
|
||||||
|
{
|
||||||
|
dup=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dup)
|
||||||
|
{
|
||||||
|
j--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//printf("keylen-%d\t%d\t%s\n", str_len, str.length(),rc);
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr.push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REGION ALLOCATION
|
||||||
|
**/
|
||||||
|
pageid_t alloc_region(int xid, void *conf)
|
||||||
|
{
|
||||||
|
RegionAllocConf_t* a = (RegionAllocConf_t*)conf;
|
||||||
|
|
||||||
|
if(a->nextPage == a->endOfRegion) {
|
||||||
|
if(a->regionList.size == -1) {
|
||||||
|
//DEBUG("nextPage: %lld\n", a->nextPage);
|
||||||
|
a->regionList = TarrayListAlloc(xid, 1, 4, sizeof(pageid_t));
|
||||||
|
DEBUG("regionList.page: %lld\n", a->regionList.page);
|
||||||
|
DEBUG("regionList.slot: %d\n", a->regionList.slot);
|
||||||
|
DEBUG("regionList.size: %lld\n", a->regionList.size);
|
||||||
|
|
||||||
|
a->regionCount = 0;
|
||||||
|
}
|
||||||
|
DEBUG("{%lld <- alloc region arraylist}\n", a->regionList.page);
|
||||||
|
TarrayListExtend(xid,a->regionList,1);
|
||||||
|
a->regionList.slot = a->regionCount;
|
||||||
|
DEBUG("region lst slot %d\n",a->regionList.slot);
|
||||||
|
a->regionCount++;
|
||||||
|
DEBUG("region count %lld\n",a->regionCount);
|
||||||
|
a->nextPage = TregionAlloc(xid, a->regionSize,12);
|
||||||
|
DEBUG("next page %lld\n",a->nextPage);
|
||||||
|
a->endOfRegion = a->nextPage + a->regionSize;
|
||||||
|
Tset(xid,a->regionList,&a->nextPage);
|
||||||
|
DEBUG("next page %lld\n",a->nextPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("%lld ?= %lld\n", a->nextPage,a->endOfRegion);
|
||||||
|
pageid_t ret = a->nextPage;
|
||||||
|
// Ensure the page is in buffer cache without accessing disk (this
|
||||||
|
// sets it to clean and all zeros if the page is not in cache).
|
||||||
|
// Hopefully, future reads will get a cache hit, and avoid going to
|
||||||
|
// disk.
|
||||||
|
|
||||||
|
Page * p = loadUninitializedPage(xid, ret);
|
||||||
|
releasePage(p);
|
||||||
|
DEBUG("ret %lld\n",ret);
|
||||||
|
(a->nextPage)++;
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pageid_t alloc_region_rid(int xid, void * ridp) {
|
||||||
|
recordid rid = *(recordid*)ridp;
|
||||||
|
RegionAllocConf_t conf;
|
||||||
|
Tread(xid,rid,&conf);
|
||||||
|
pageid_t ret = alloc_region(xid,&conf);
|
||||||
|
DEBUG("{%lld <- alloc region extend}\n", conf.regionList.page);
|
||||||
|
|
||||||
|
Tset(xid,rid,&conf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
std::vector<std::string> data_arr;
|
||||||
|
std::vector<std::string> key_arr;
|
||||||
|
preprandstr(NUM_ENTRIES, data_arr, 5*4096, true);
|
||||||
|
preprandstr(NUM_ENTRIES+200, key_arr, 50, true);//well i can handle upto 200
|
||||||
|
|
||||||
|
std::sort(key_arr.begin(), key_arr.end(), &mycmp);
|
||||||
|
|
||||||
|
removeduplicates(key_arr);
|
||||||
|
if(key_arr.size() > NUM_ENTRIES)
|
||||||
|
key_arr.erase(key_arr.begin()+NUM_ENTRIES, key_arr.end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr.size();
|
||||||
|
|
||||||
|
if(data_arr.size() > NUM_ENTRIES)
|
||||||
|
data_arr.erase(data_arr.begin()+NUM_ENTRIES, data_arr.end());
|
||||||
|
|
||||||
|
//for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
//{
|
||||||
|
// printf("%s\t", arr[i].c_str());
|
||||||
|
// int keylen = arr[i].length()+1;
|
||||||
|
// printf("%d\n", keylen);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
recordid alloc_state = Talloc(xid,sizeof(RegionAllocConf_t));
|
||||||
|
|
||||||
|
Tset(xid,alloc_state, &logtree::REGION_ALLOC_STATIC_INITIALIZER);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int pcount = 10;
|
||||||
|
int dpages = 0;
|
||||||
|
DataPage<datatuple> *dp=0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = key_arr[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
newtuple.key[j] = key_arr[i][j];
|
||||||
|
newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
uint32_t datalen = data_arr[i].length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
for(int j=0; j<datalen-1; j++)
|
||||||
|
newtuple.data[j] = data_arr[i][j];
|
||||||
|
newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
if(dp==NULL || !dp->append(xid, newtuple))
|
||||||
|
{
|
||||||
|
dpages++;
|
||||||
|
if(dp)
|
||||||
|
delete dp;
|
||||||
|
|
||||||
|
dp = new DataPage<datatuple>(xid, pcount, &DataPage<datatuple>::dp_alloc_region_rid, &alloc_state );
|
||||||
|
|
||||||
|
if(!dp->append(xid, newtuple))
|
||||||
|
{
|
||||||
|
delete dp;
|
||||||
|
dp = new DataPage<datatuple>(xid, pcount, &DataPage<datatuple>::dp_alloc_region_rid, &alloc_state );
|
||||||
|
assert(dp->append(xid, newtuple));
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.push_back(dp->get_start_pid());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Total data set length: %d\n", datasize);
|
||||||
|
printf("Storage utilization: %.2f\n", (datasize+.0) / (PAGE_SIZE * pcount * dpages));
|
||||||
|
printf("Number of datapages: %d\n", dpages);
|
||||||
|
printf("Writes complete.\n");
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Reading %d tuples\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
|
||||||
|
int tuplenum = 0;
|
||||||
|
for(int i = 0; i < dpages ; i++)
|
||||||
|
{
|
||||||
|
DataPage<datatuple> dp(xid, dsp[i]);
|
||||||
|
DataPage<datatuple>::RecordIterator itr = dp.begin();
|
||||||
|
datatuple *dt=0;
|
||||||
|
while( (dt=itr.getnext(xid)) != NULL)
|
||||||
|
{
|
||||||
|
assert(*(dt->keylen) == key_arr[tuplenum].length()+1);
|
||||||
|
assert(*(dt->datalen) == data_arr[tuplenum].length()+1);
|
||||||
|
tuplenum++;
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
dt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Reads completed.\n");
|
||||||
|
/*
|
||||||
|
|
||||||
|
int64_t count = 0;
|
||||||
|
lladdIterator_t * it = logtreeIterator::open(xid, tree);
|
||||||
|
|
||||||
|
while(logtreeIterator::next(xid, it)) {
|
||||||
|
byte * key;
|
||||||
|
byte **key_ptr = &key;
|
||||||
|
int keysize = logtreeIterator::key(xid, it, (byte**)key_ptr);
|
||||||
|
|
||||||
|
pageid_t *value;
|
||||||
|
pageid_t **value_ptr = &value;
|
||||||
|
int valsize = lsmTreeIterator_value(xid, it, (byte**)value_ptr);
|
||||||
|
//printf("keylen %d key %s\n", keysize, (char*)(key)) ;
|
||||||
|
assert(valsize == sizeof(pageid_t));
|
||||||
|
assert(!mycmp(std::string((char*)key), arr[count]) && !mycmp(arr[count],std::string((char*)key)));
|
||||||
|
assert(keysize == arr[count].length()+1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assert(count == NUM_ENTRIES);
|
||||||
|
|
||||||
|
logtreeIterator::close(xid, it);
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter(10000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
39
check_gen.cpp
Normal file
39
check_gen.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
// PAGELAYOUT::initPageLayout();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
//ltable.startTable();
|
||||||
|
|
||||||
|
// lsmTableHandle<PAGELAYOUT>* h = TlsmTableStart<PAGELAYOUT>(lsmTable, INVALID_COL);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
logtreeIterator::open(xid,ltable.get_tree_c2()->get_root_rec() );
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
276
check_logtable.cpp
Normal file
276
check_logtable.cpp
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//template class DataPage<datatuple>;
|
||||||
|
template class treeIterator<datatuple>;
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> &arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr.size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp(arr[i], arr[i-1]) || mycmp(arr[i-1], arr[i])))
|
||||||
|
arr.erase(arr.begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> &arr, int avg_len=50, bool duplicates_allowed=false)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
//make sure there is no duplicate key
|
||||||
|
if(!duplicates_allowed)
|
||||||
|
{
|
||||||
|
bool dup = false;
|
||||||
|
for(int i=0; i<j; i++)
|
||||||
|
if(! (mycmp(arr[i], str) || mycmp(str, arr[i])))
|
||||||
|
{
|
||||||
|
dup=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dup)
|
||||||
|
{
|
||||||
|
j--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//printf("keylen-%d\t%d\t%s\n", str_len, str.length(),rc);
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr.push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
int pcount = 5;
|
||||||
|
ltable.set_fixed_page_count(pcount);
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
logtree *lt = ltable.get_tree_c1();
|
||||||
|
|
||||||
|
recordid tree_root = lt->get_root_rec();
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> data_arr;
|
||||||
|
std::vector<std::string> key_arr;
|
||||||
|
preprandstr(NUM_ENTRIES, data_arr, 5*4096, true);
|
||||||
|
preprandstr(NUM_ENTRIES+200, key_arr, 50, true);//well i can handle upto 200
|
||||||
|
|
||||||
|
std::sort(key_arr.begin(), key_arr.end(), &mycmp);
|
||||||
|
|
||||||
|
removeduplicates(key_arr);
|
||||||
|
if(key_arr.size() > NUM_ENTRIES)
|
||||||
|
key_arr.erase(key_arr.begin()+NUM_ENTRIES, key_arr.end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr.size();
|
||||||
|
|
||||||
|
if(data_arr.size() > NUM_ENTRIES)
|
||||||
|
data_arr.erase(data_arr.begin()+NUM_ENTRIES, data_arr.end());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
|
||||||
|
int dpages = 0;
|
||||||
|
int npages = 0;
|
||||||
|
DataPage<datatuple> *dp=0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = key_arr[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
newtuple.key[j] = key_arr[i][j];
|
||||||
|
newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
uint32_t datalen = data_arr[i].length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
for(int j=0; j<datalen-1; j++)
|
||||||
|
newtuple.data[j] = data_arr[i][j];
|
||||||
|
newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
// printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
// *newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
// *newtuple.datalen);
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
if(dp == NULL)
|
||||||
|
{
|
||||||
|
dp = ltable.insertTuple(xid, newtuple, ltable.get_dpstate1(), lt);
|
||||||
|
dpages++;
|
||||||
|
dsp.push_back(dp->get_start_pid());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!dp->append(xid, newtuple))
|
||||||
|
{
|
||||||
|
npages += dp->get_page_count();
|
||||||
|
delete dp;
|
||||||
|
dp = ltable.insertTuple(xid, newtuple, ltable.get_dpstate1(), lt);
|
||||||
|
dpages++;
|
||||||
|
dsp.push_back(dp->get_start_pid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(newtuple.key);
|
||||||
|
free(newtuple.data);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
lt->print_tree(xid);
|
||||||
|
|
||||||
|
printf("Total data set length: %d\n", datasize);
|
||||||
|
printf("Storage utilization: %.2f\n", (datasize+.0) / (PAGE_SIZE * npages));
|
||||||
|
printf("Number of datapages: %d\n", dpages);
|
||||||
|
printf("Writes complete.\n");
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Sequentially reading %d tuples\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
|
||||||
|
int tuplenum = 0;
|
||||||
|
treeIterator<datatuple> tree_itr(tree_root);
|
||||||
|
|
||||||
|
|
||||||
|
datatuple *dt=0;
|
||||||
|
while( (dt=tree_itr.getnext()) != NULL)
|
||||||
|
{
|
||||||
|
assert(*(dt->keylen) == key_arr[tuplenum].length()+1);
|
||||||
|
assert(*(dt->datalen) == data_arr[tuplenum].length()+1);
|
||||||
|
tuplenum++;
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
dt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(tuplenum == key_arr.size());
|
||||||
|
|
||||||
|
printf("Sequential Reads completed.\n");
|
||||||
|
|
||||||
|
int rrsize=key_arr.size() / 3;
|
||||||
|
printf("Stage 3: Randomly reading %d tuples by key\n", rrsize);
|
||||||
|
|
||||||
|
for(int i=0; i<rrsize; i++)
|
||||||
|
{
|
||||||
|
//randomly pick a key
|
||||||
|
int ri = rand()%key_arr.size();
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = key_arr[ri].length()+1;
|
||||||
|
datatuple::key_t rkey = (datatuple::key_t) malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
rkey[j] = key_arr[ri][j];
|
||||||
|
rkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
datatuple *dt = ltable.findTuple(xid, rkey, keylen, lt);
|
||||||
|
|
||||||
|
assert(dt!=0);
|
||||||
|
assert(*(dt->keylen) == key_arr[ri].length()+1);
|
||||||
|
assert(*(dt->datalen) == data_arr[ri].length()+1);
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
dt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Random Reads completed.\n");
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter(15000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
331
check_logtree.cpp
Normal file
331
check_logtree.cpp
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define LOG_NAME "check_logTree.log"
|
||||||
|
#define NUM_ENTRIES_A 10000
|
||||||
|
#define NUM_ENTRIES_B 10
|
||||||
|
#define NUM_ENTRIES_C 0
|
||||||
|
|
||||||
|
#define OFFSET (NUM_ENTRIES * 10)
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> &arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = rand()%100 + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
//make sure there is no duplicate key
|
||||||
|
bool dup = false;
|
||||||
|
for(int i=0; i<j; i++)
|
||||||
|
if(! (mycmp(arr[i], str) || mycmp(str, arr[i])))
|
||||||
|
{
|
||||||
|
dup=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dup)
|
||||||
|
{
|
||||||
|
j--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//printf("keylen-%d\t%d\t%s\n", str_len, str.length(),rc);
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr.push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void insertProbeIter_str(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
logtree *lt = ltable.get_tree_c1();
|
||||||
|
|
||||||
|
recordid tree = lt->get_root_rec();
|
||||||
|
|
||||||
|
long oldpagenum = -1;
|
||||||
|
|
||||||
|
std::vector<std::string> arr;
|
||||||
|
preprandstr(NUM_ENTRIES, arr);
|
||||||
|
std::sort(arr.begin(), arr.end(), &mycmp);
|
||||||
|
|
||||||
|
//for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
//{
|
||||||
|
// printf("%s\t", arr[i].c_str());
|
||||||
|
// int keylen = arr[i].length()+1;
|
||||||
|
// printf("%d\n", keylen);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
int keylen = arr[i].length()+1;
|
||||||
|
byte *currkey = (byte*)malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
currkey[j] = arr[i][j];
|
||||||
|
currkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//printf("\n#########\ni=%d\nkey:\t%s\nkeylen:%d\n",i,((char*)currkey),keylen);
|
||||||
|
long pagenum = logtree::findPage(xid, tree, currkey, keylen);
|
||||||
|
//printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == -1 || pagenum == oldpagenum || oldpagenum == -1);
|
||||||
|
//printf("TlsmAppendPage %d\n",i);
|
||||||
|
|
||||||
|
recordid rid = lt->get_tree_state();
|
||||||
|
RegionAllocConf_t alloc_conf;
|
||||||
|
Tread(xid,rid,&alloc_conf);
|
||||||
|
|
||||||
|
logtree::appendPage(xid, tree, lt->lastLeaf, currkey, keylen, lt->alloc_region, &alloc_conf, i + OFFSET);
|
||||||
|
|
||||||
|
//DEBUG("{%lld <- alloc region extend}\n", conf.regionList.page);
|
||||||
|
// XXX get rid of Tset by storing next page in memory, and losing it
|
||||||
|
// on crash.
|
||||||
|
Tset(xid,rid,&alloc_conf);
|
||||||
|
|
||||||
|
|
||||||
|
pagenum = logtree::findPage(xid, tree, currkey,keylen);
|
||||||
|
oldpagenum = pagenum;
|
||||||
|
//printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == i + OFFSET);
|
||||||
|
free(currkey);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Writes complete.");
|
||||||
|
|
||||||
|
tree = lt->get_root_rec();
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
lt->print_tree(xid);
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++) {
|
||||||
|
int keylen = arr[i].length()+1;
|
||||||
|
byte *currkey = (byte*)malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
currkey[j] = arr[i][j];
|
||||||
|
currkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//printf("\n#########\ni=%d\nkey:\t%s\nkeylen:%d\n",i,((char*)currkey),keylen);
|
||||||
|
long pagenum = logtree::findPage(xid, tree, currkey, keylen);
|
||||||
|
//printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == i + OFFSET);
|
||||||
|
free(currkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 3: Iterating over %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
|
||||||
|
int64_t count = 0;
|
||||||
|
lladdIterator_t * it = logtreeIterator::open(xid, tree);
|
||||||
|
|
||||||
|
while(logtreeIterator::next(xid, it)) {
|
||||||
|
byte * key;
|
||||||
|
byte **key_ptr = &key;
|
||||||
|
int keysize = logtreeIterator::key(xid, it, (byte**)key_ptr);
|
||||||
|
|
||||||
|
pageid_t *value;
|
||||||
|
pageid_t **value_ptr = &value;
|
||||||
|
int valsize = lsmTreeIterator_value(xid, it, (byte**)value_ptr);
|
||||||
|
//printf("keylen %d key %s\n", keysize, (char*)(key)) ;
|
||||||
|
assert(valsize == sizeof(pageid_t));
|
||||||
|
assert(!mycmp(std::string((char*)key), arr[count]) && !mycmp(arr[count],std::string((char*)key)));
|
||||||
|
assert(keysize == arr[count].length()+1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assert(count == NUM_ENTRIES);
|
||||||
|
|
||||||
|
logtreeIterator::close(xid, it);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void insertProbeIter_int(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
logtree *lt = ltable.get_tree_c1();
|
||||||
|
|
||||||
|
recordid tree = lt->get_root_rec();
|
||||||
|
|
||||||
|
long oldpagenum = -1;
|
||||||
|
|
||||||
|
for(int32_t i = 0; i < NUM_ENTRIES; i++) {
|
||||||
|
int keylen = sizeof(int32_t);
|
||||||
|
byte *currkey = (byte*)malloc(keylen);
|
||||||
|
memcpy(currkey, (byte*)(&i), keylen);
|
||||||
|
//currkey[]='\0';
|
||||||
|
|
||||||
|
printf("\n#########\ni=%d\nkey:\t%d\nkeylen:%d\n",i,*((int32_t*)currkey),keylen);
|
||||||
|
long pagenum = logtree::findPage(xid, tree, currkey, keylen);
|
||||||
|
printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == -1 || pagenum == oldpagenum || oldpagenum == -1);
|
||||||
|
printf("TlsmAppendPage %d\n",i);
|
||||||
|
|
||||||
|
recordid rid = lt->get_tree_state();
|
||||||
|
RegionAllocConf_t alloc_conf;
|
||||||
|
Tread(xid,rid,&alloc_conf);
|
||||||
|
|
||||||
|
logtree::appendPage(xid, tree, lt->lastLeaf, currkey, keylen, lt->alloc_region, &alloc_conf, i + OFFSET);
|
||||||
|
|
||||||
|
//DEBUG("{%lld <- alloc region extend}\n", conf.regionList.page);
|
||||||
|
// XXX get rid of Tset by storing next page in memory, and losing it
|
||||||
|
// on crash.
|
||||||
|
Tset(xid,rid,&alloc_conf);
|
||||||
|
|
||||||
|
|
||||||
|
pagenum = logtree::findPage(xid, tree, currkey,keylen);
|
||||||
|
oldpagenum = pagenum;
|
||||||
|
printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == i + OFFSET);
|
||||||
|
free(currkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Writes complete.");
|
||||||
|
|
||||||
|
tree = lt->get_root_rec();
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
lt->print_tree(xid);
|
||||||
|
|
||||||
|
for(int32_t i = 1; i < NUM_ENTRIES; i++) {
|
||||||
|
int keylen = sizeof(int32_t);
|
||||||
|
byte *currkey = (byte*)malloc(keylen);
|
||||||
|
memcpy(currkey, (byte*)(&i), keylen);
|
||||||
|
|
||||||
|
printf("\n#########\ni=%d\nkey:\t%d\nkeylen:%d\n",i,*((int32_t*)currkey),keylen);
|
||||||
|
long pagenum = logtree::findPage(xid, tree, currkey, keylen);
|
||||||
|
printf("pagenum:%d\n", pagenum);
|
||||||
|
assert(pagenum == i + OFFSET);
|
||||||
|
free(currkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
int64_t count = 0;
|
||||||
|
|
||||||
|
lladdIterator_t * it = lsmTreeIterator_open(xid, tree);
|
||||||
|
|
||||||
|
while(lsmTreeIterator_next(xid, it)) {
|
||||||
|
lsmkey_t * key;
|
||||||
|
lsmkey_t **key_ptr = &key;
|
||||||
|
int size = lsmTreeIterator_key(xid, it, (byte**)key_ptr);
|
||||||
|
assert(size == sizeof(lsmkey_t));
|
||||||
|
long *value;
|
||||||
|
long **value_ptr = &value;
|
||||||
|
size = lsmTreeIterator_value(xid, it, (byte**)value_ptr);
|
||||||
|
assert(size == sizeof(pageid_t));
|
||||||
|
assert(*key + OFFSET == *value);
|
||||||
|
assert(*key == count);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assert(count == NUM_ENTRIES);
|
||||||
|
|
||||||
|
lsmTreeIterator_close(xid, it);
|
||||||
|
|
||||||
|
*/
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter_str(NUM_ENTRIES_A);
|
||||||
|
//insertProbeIter_int(NUM_ENTRIES_A);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
246
check_merge.cpp
Normal file
246
check_merge.cpp
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "merger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> *arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr->size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp((*arr)[i], (*arr)[i-1]) || mycmp((*arr)[i-1], (*arr)[i])))
|
||||||
|
arr->erase(arr->begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> *arr, int avg_len=50)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr->push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
//data generation
|
||||||
|
std::vector<std::string> * data_arr = new std::vector<std::string>;
|
||||||
|
std::vector<std::string> * key_arr = new std::vector<std::string>;
|
||||||
|
|
||||||
|
preprandstr(NUM_ENTRIES, data_arr, 10*8192);
|
||||||
|
preprandstr(NUM_ENTRIES+200, key_arr, 100);
|
||||||
|
|
||||||
|
std::sort(key_arr->begin(), key_arr->end(), &mycmp);
|
||||||
|
|
||||||
|
removeduplicates(key_arr);
|
||||||
|
if(key_arr->size() > NUM_ENTRIES)
|
||||||
|
key_arr->erase(key_arr->begin()+NUM_ENTRIES, key_arr->end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr->size();
|
||||||
|
|
||||||
|
if(data_arr->size() > NUM_ENTRIES)
|
||||||
|
data_arr->erase(data_arr->begin()+NUM_ENTRIES, data_arr->end());
|
||||||
|
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
merge_scheduler mscheduler;
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
int pcount = 5;
|
||||||
|
ltable.set_fixed_page_count(pcount);
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
int lindex = mscheduler.addlogtable(<able);
|
||||||
|
ltable.setMergeData(mscheduler.getMergeData(lindex));
|
||||||
|
|
||||||
|
mscheduler.startlogtable(lindex);
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
struct timeval start_tv, stop_tv, ti_st, ti_end;
|
||||||
|
double insert_time = 0;
|
||||||
|
int dpages = 0;
|
||||||
|
int npages = 0;
|
||||||
|
DataPage<datatuple> *dp=0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
gettimeofday(&start_tv,0);
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = (*key_arr)[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)newtuple.key, (*key_arr)[i].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
// newtuple.key[j] = (*key_arr)[i][j];
|
||||||
|
//newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
uint32_t datalen = (*data_arr)[i].length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
memcpy((byte*)newtuple.data, (*data_arr)[i].c_str(), datalen);
|
||||||
|
// for(int j=0; j<datalen-1; j++)
|
||||||
|
// newtuple.data[j] = (*data_arr)[i][j];
|
||||||
|
// newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
ltable.insertTuple(newtuple);
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(newtuple.key);
|
||||||
|
free(newtuple.data);
|
||||||
|
|
||||||
|
}
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("insert time: %6.1f\n", insert_time);
|
||||||
|
printf("insert time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
//ltable.get_tree_c1()->print_tree(xid);
|
||||||
|
printf("datasize: %d\n", datasize);
|
||||||
|
//sleep(20);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys:\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int found_tuples=0;
|
||||||
|
for(int i=NUM_ENTRIES-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
int ri = i;
|
||||||
|
//printf("key index%d\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = (*key_arr)[ri].length()+1;
|
||||||
|
datatuple::key_t rkey = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)rkey, (*key_arr)[ri].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
//rkey[j] = (*key_arr)[ri][j];
|
||||||
|
//rkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
datatuple *dt = ltable.findTuple(xid, rkey, keylen);
|
||||||
|
|
||||||
|
assert(dt!=0);
|
||||||
|
//if(dt!=0)
|
||||||
|
{
|
||||||
|
found_tuples++;
|
||||||
|
assert(*(dt->keylen) == (*key_arr)[ri].length()+1);
|
||||||
|
assert(*(dt->datalen) == (*data_arr)[ri].length()+1);
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
dt = 0;
|
||||||
|
free(rkey);
|
||||||
|
}
|
||||||
|
printf("found %d\n", found_tuples);
|
||||||
|
|
||||||
|
key_arr->clear();
|
||||||
|
data_arr->clear();
|
||||||
|
delete key_arr;
|
||||||
|
delete data_arr;
|
||||||
|
|
||||||
|
mscheduler.shutdown();
|
||||||
|
printf("merge threads finished.\n");
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("run time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter(5000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
264
check_mergelarge.cpp
Normal file
264
check_mergelarge.cpp
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "merger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> *arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr->size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp((*arr)[i], (*arr)[i-1]) || mycmp((*arr)[i-1], (*arr)[i])))
|
||||||
|
arr->erase(arr->begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void getnextdata(std::string &data, int avg_len)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
data = std::string(str_len, rand()%10+48);
|
||||||
|
/*
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
data = std::string(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> *arr, int avg_len=50)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr->push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
unlink("storefile.txt");
|
||||||
|
unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
//data generation
|
||||||
|
// std::vector<std::string> * data_arr = new std::vector<std::string>;
|
||||||
|
std::vector<std::string> * key_arr = new std::vector<std::string>;
|
||||||
|
|
||||||
|
// preprandstr(NUM_ENTRIES, data_arr, 10*8192);
|
||||||
|
preprandstr(NUM_ENTRIES+200, key_arr, 100);
|
||||||
|
|
||||||
|
std::sort(key_arr->begin(), key_arr->end(), &mycmp);
|
||||||
|
|
||||||
|
removeduplicates(key_arr);
|
||||||
|
if(key_arr->size() > NUM_ENTRIES)
|
||||||
|
key_arr->erase(key_arr->begin()+NUM_ENTRIES, key_arr->end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr->size();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
merge_scheduler mscheduler;
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
int pcount = 100;
|
||||||
|
ltable.set_fixed_page_count(pcount);
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
//xid = Tbegin();
|
||||||
|
|
||||||
|
int lindex = mscheduler.addlogtable(<able);
|
||||||
|
ltable.setMergeData(mscheduler.getMergeData(lindex));
|
||||||
|
|
||||||
|
mscheduler.startlogtable(lindex);
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
struct timeval start_tv, stop_tv, ti_st, ti_end;
|
||||||
|
double insert_time = 0;
|
||||||
|
int dpages = 0;
|
||||||
|
int npages = 0;
|
||||||
|
DataPage<datatuple> *dp=0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
gettimeofday(&start_tv,0);
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = (*key_arr)[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)newtuple.key, (*key_arr)[i].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
// newtuple.key[j] = (*key_arr)[i][j];
|
||||||
|
//newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
std::string ditem;
|
||||||
|
getnextdata(ditem, 10*8192);
|
||||||
|
uint32_t datalen = ditem.length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
memcpy((byte*)newtuple.data, ditem.c_str(), datalen);
|
||||||
|
// for(int j=0; j<datalen-1; j++)
|
||||||
|
// newtuple.data[j] = (*data_arr)[i][j];
|
||||||
|
// newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
ltable.insertTuple(newtuple);
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(newtuple.key);
|
||||||
|
free(newtuple.data);
|
||||||
|
|
||||||
|
}
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("insert time: %6.1f\n", insert_time);
|
||||||
|
printf("insert time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
//ltable.get_tree_c1()->print_tree(xid);
|
||||||
|
printf("datasize: %lld\n", datasize);
|
||||||
|
//sleep(20);
|
||||||
|
|
||||||
|
/*
|
||||||
|
//Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys:\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int found_tuples=0;
|
||||||
|
for(int i=NUM_ENTRIES-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
int ri = i;
|
||||||
|
//printf("key index%d\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = (*key_arr)[ri].length()+1;
|
||||||
|
datatuple::key_t rkey = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)rkey, (*key_arr)[ri].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
//rkey[j] = (*key_arr)[ri][j];
|
||||||
|
//rkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
datatuple *dt = ltable.findTuple(xid, rkey, keylen);
|
||||||
|
|
||||||
|
assert(dt!=0);
|
||||||
|
//if(dt!=0)
|
||||||
|
{
|
||||||
|
found_tuples++;
|
||||||
|
assert(*(dt->keylen) == (*key_arr)[ri].length()+1);
|
||||||
|
//assert(*(dt->datalen) == (*data_arr)[ri].length()+1);
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
dt = 0;
|
||||||
|
free(rkey);
|
||||||
|
}
|
||||||
|
printf("found %d\n", found_tuples);
|
||||||
|
|
||||||
|
key_arr->clear();
|
||||||
|
//data_arr->clear();
|
||||||
|
delete key_arr;
|
||||||
|
//delete data_arr;
|
||||||
|
*/
|
||||||
|
|
||||||
|
mscheduler.shutdown();
|
||||||
|
printf("merge threads finished.\n");
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("run time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
//Tcommit(xid);
|
||||||
|
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter(25000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
409
check_mergetuple.cpp
Normal file
409
check_mergetuple.cpp
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "merger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> *arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr->size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp((*arr)[i], (*arr)[i-1]) || mycmp((*arr)[i-1], (*arr)[i])))
|
||||||
|
arr->erase(arr->begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void getnextdata(std::string &data, int avg_len)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
data = std::string(str_len, rand()%10+48);
|
||||||
|
/*
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
data = std::string(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> *arr, int avg_len=50)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr->push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
//unlink("storefile.txt");
|
||||||
|
//unlink("logfile.txt");
|
||||||
|
|
||||||
|
sync();
|
||||||
|
double delete_freq = .05;
|
||||||
|
double update_freq = .15;
|
||||||
|
|
||||||
|
//data generation
|
||||||
|
typedef std::vector<std::string> key_v_t;
|
||||||
|
const static int max_partition_size = 100000;
|
||||||
|
int KEY_LEN = 100;
|
||||||
|
std::vector<key_v_t*> *key_v_list = new std::vector<key_v_t*>;
|
||||||
|
int list_size = NUM_ENTRIES / max_partition_size + 1;
|
||||||
|
for(int i =0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
key_v_t * key_arr = new key_v_t;
|
||||||
|
if(NUM_ENTRIES < max_partition_size*(i+1))
|
||||||
|
preprandstr(NUM_ENTRIES-max_partition_size*i, key_arr, KEY_LEN);
|
||||||
|
else
|
||||||
|
preprandstr(max_partition_size, key_arr, KEY_LEN);
|
||||||
|
|
||||||
|
std::sort(key_arr->begin(), key_arr->end(), &mycmp);
|
||||||
|
key_v_list->push_back(key_arr);
|
||||||
|
printf("size partition %d is %d\n", i+1, key_arr->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
key_v_t * key_arr = new key_v_t;
|
||||||
|
|
||||||
|
std::vector<key_v_t::iterator*> iters;
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
iters.push_back(new key_v_t::iterator((*key_v_list)[i]->begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int lc = 0;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int list_index = -1;
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
if(*iters[i] == (*key_v_list)[i]->end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list_index == -1 || mycmp(**iters[i], **iters[list_index]))
|
||||||
|
list_index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(list_index == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(key_arr->size() == 0 || mycmp(key_arr->back(), **iters[list_index]))
|
||||||
|
key_arr->push_back(**iters[list_index]);
|
||||||
|
|
||||||
|
(*iters[list_index])++;
|
||||||
|
lc++;
|
||||||
|
if(lc % max_partition_size == 0)
|
||||||
|
printf("%d/%d completed.\n", lc, NUM_ENTRIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
(*key_v_list)[i]->clear();
|
||||||
|
delete (*key_v_list)[i];
|
||||||
|
delete iters[i];
|
||||||
|
}
|
||||||
|
key_v_list->clear();
|
||||||
|
delete key_v_list;
|
||||||
|
|
||||||
|
// preprandstr(NUM_ENTRIES, data_arr, 10*8192);
|
||||||
|
|
||||||
|
printf("key arr size: %d\n", key_arr->size());
|
||||||
|
|
||||||
|
//removeduplicates(key_arr);
|
||||||
|
if(key_arr->size() > NUM_ENTRIES)
|
||||||
|
key_arr->erase(key_arr->begin()+NUM_ENTRIES, key_arr->end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr->size();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
merge_scheduler mscheduler;
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
int pcount = 40;
|
||||||
|
ltable.set_fixed_page_count(pcount);
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
int lindex = mscheduler.addlogtable(<able);
|
||||||
|
ltable.setMergeData(mscheduler.getMergeData(lindex));
|
||||||
|
|
||||||
|
mscheduler.startlogtable(lindex);
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
struct timeval start_tv, stop_tv, ti_st, ti_end;
|
||||||
|
double insert_time = 0;
|
||||||
|
int dpages = 0;
|
||||||
|
int npages = 0;
|
||||||
|
int delcount = 0, upcount = 0;
|
||||||
|
DataPage<datatuple> *dp=0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
std::vector<int> del_list;
|
||||||
|
gettimeofday(&start_tv,0);
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = (*key_arr)[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)newtuple.key, (*key_arr)[i].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
// newtuple.key[j] = (*key_arr)[i][j];
|
||||||
|
//newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
std::string ditem;
|
||||||
|
getnextdata(ditem, 8192);
|
||||||
|
uint32_t datalen = ditem.length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
memcpy((byte*)newtuple.data, ditem.c_str(), datalen);
|
||||||
|
// for(int j=0; j<datalen-1; j++)
|
||||||
|
// newtuple.data[j] = (*data_arr)[i][j];
|
||||||
|
// newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
ltable.insertTuple(newtuple);
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(newtuple.key);
|
||||||
|
free(newtuple.data);
|
||||||
|
|
||||||
|
double rval = ((rand() % 100)+.0)/100;
|
||||||
|
if( rval < delete_freq) //delete a key
|
||||||
|
{
|
||||||
|
int del_index = i - (rand()%50); //delete one of the last inserted 50 elements
|
||||||
|
if(del_index >= 0 && std::find(del_list.begin(), del_list.end(), del_index) == del_list.end())
|
||||||
|
{
|
||||||
|
delcount++;
|
||||||
|
datatuple deltuple;
|
||||||
|
keylen = (*key_arr)[del_index].length()+1;
|
||||||
|
deltuple.keylen = &keylen;
|
||||||
|
|
||||||
|
deltuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)deltuple.key, (*key_arr)[del_index].c_str(), keylen);
|
||||||
|
|
||||||
|
deltuple.datalen = &datalen;
|
||||||
|
deltuple.setDelete();
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
ltable.insertTuple(deltuple);
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(deltuple.key);
|
||||||
|
|
||||||
|
del_list.push_back(del_index);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(rval < delete_freq + update_freq) //update a record
|
||||||
|
{
|
||||||
|
int up_index = i - (rand()%50); //update one of the last inserted 50 elements
|
||||||
|
if(up_index >= 0 && std::find(del_list.begin(), del_list.end(), up_index) == del_list.end())
|
||||||
|
{//only update non-deleted elements
|
||||||
|
upcount++;
|
||||||
|
datatuple uptuple;
|
||||||
|
keylen = (*key_arr)[up_index].length()+1;
|
||||||
|
uptuple.keylen = &keylen;
|
||||||
|
|
||||||
|
uptuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)uptuple.key, (*key_arr)[up_index].c_str(), keylen);
|
||||||
|
|
||||||
|
getnextdata(ditem, 512);
|
||||||
|
datalen = ditem.length()+1;
|
||||||
|
uptuple.datalen = &datalen;
|
||||||
|
uptuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
memcpy((byte*)uptuple.data, ditem.c_str(), datalen);
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
ltable.insertTuple(uptuple);
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(uptuple.key);
|
||||||
|
free(uptuple.data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("insert time: %6.1f\n", insert_time);
|
||||||
|
printf("insert time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
printf("#deletions: %d\n#updates: %d\n", delcount, upcount);
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
//ltable.get_tree_c1()->print_tree(xid);
|
||||||
|
printf("datasize: %lld\n", datasize);
|
||||||
|
//sleep(20);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys:\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int found_tuples=0;
|
||||||
|
for(int i=NUM_ENTRIES-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
int ri = i;
|
||||||
|
//printf("key index%d\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = (*key_arr)[ri].length()+1;
|
||||||
|
datatuple::key_t rkey = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)rkey, (*key_arr)[ri].c_str(), keylen);
|
||||||
|
//for(int j=0; j<keylen-1; j++)
|
||||||
|
//rkey[j] = (*key_arr)[ri][j];
|
||||||
|
//rkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
datatuple *dt = ltable.findTuple(xid, rkey, keylen);
|
||||||
|
|
||||||
|
if(std::find(del_list.begin(), del_list.end(), i) == del_list.end())
|
||||||
|
{
|
||||||
|
assert(dt!=0);
|
||||||
|
assert(!dt->isDelete());
|
||||||
|
found_tuples++;
|
||||||
|
assert(*(dt->keylen) == (*key_arr)[ri].length()+1);
|
||||||
|
//assert(*(dt->datalen) == (*data_arr)[ri].length()+1);
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dt!=0)
|
||||||
|
{
|
||||||
|
assert(*(dt->keylen) == (*key_arr)[ri].length()+1);
|
||||||
|
assert(dt->isDelete());
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dt = 0;
|
||||||
|
free(rkey);
|
||||||
|
}
|
||||||
|
printf("found %d\n", found_tuples);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
key_arr->clear();
|
||||||
|
//data_arr->clear();
|
||||||
|
delete key_arr;
|
||||||
|
//delete data_arr;
|
||||||
|
|
||||||
|
mscheduler.shutdown();
|
||||||
|
printf("merge threads finished.\n");
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("run time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//insertProbeIter(25000);
|
||||||
|
insertProbeIter(400000);
|
||||||
|
/*
|
||||||
|
insertProbeIter(5000);
|
||||||
|
insertProbeIter(2500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(100);
|
||||||
|
insertProbeIter(10);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
214
check_rbtree.cpp
Normal file
214
check_rbtree.cpp
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "merger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> &arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr.size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp(arr[i], arr[i-1]) || mycmp(arr[i-1], arr[i])))
|
||||||
|
arr.erase(arr.begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> &arr, int avg_len=50, bool duplicates_allowed=false)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
//make sure there is no duplicate key
|
||||||
|
if(!duplicates_allowed)
|
||||||
|
{
|
||||||
|
bool dup = false;
|
||||||
|
for(int i=0; i<j; i++)
|
||||||
|
if(! (mycmp(arr[i], str) || mycmp(str, arr[i])))
|
||||||
|
{
|
||||||
|
dup=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dup)
|
||||||
|
{
|
||||||
|
j--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//printf("keylen-%d\t%d\t%s\n", str_len, str.length(),rc);
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr.push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
|
||||||
|
//data generation
|
||||||
|
std::vector<std::string> data_arr;
|
||||||
|
std::vector<std::string> key_arr;
|
||||||
|
preprandstr(NUM_ENTRIES, data_arr, 10*8192, true);
|
||||||
|
preprandstr(NUM_ENTRIES+200, key_arr, 100, true);
|
||||||
|
|
||||||
|
std::sort(key_arr.begin(), key_arr.end(), &mycmp);
|
||||||
|
|
||||||
|
removeduplicates(key_arr);
|
||||||
|
if(key_arr.size() > NUM_ENTRIES)
|
||||||
|
key_arr.erase(key_arr.begin()+NUM_ENTRIES, key_arr.end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr.size();
|
||||||
|
|
||||||
|
if(data_arr.size() > NUM_ENTRIES)
|
||||||
|
data_arr.erase(data_arr.begin()+NUM_ENTRIES, data_arr.end());
|
||||||
|
|
||||||
|
std::set<datatuple, datatuple> rbtree;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = key_arr[i].length()+1;
|
||||||
|
newtuple.keylen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
*newtuple.keylen = keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
newtuple.key[j] = key_arr[i][j];
|
||||||
|
newtuple.key[keylen-1]='\0';
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
uint32_t datalen = data_arr[i].length()+1;
|
||||||
|
newtuple.datalen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
*newtuple.datalen = datalen;
|
||||||
|
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
for(int j=0; j<datalen-1; j++)
|
||||||
|
newtuple.data[j] = data_arr[i][j];
|
||||||
|
newtuple.data[datalen-1]='\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
rbtree.insert(newtuple);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nTREE STRUCTURE\n");
|
||||||
|
//ltable.get_tree_c1()->print_tree(xid);
|
||||||
|
printf("datasize: %d\n", datasize);
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys:\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int found_tuples=0;
|
||||||
|
for(int i=NUM_ENTRIES-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
int ri = i;
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = key_arr[ri].length()+1;
|
||||||
|
datatuple::key_t rkey = (datatuple::key_t) malloc(keylen);
|
||||||
|
for(int j=0; j<keylen-1; j++)
|
||||||
|
rkey[j] = key_arr[ri][j];
|
||||||
|
rkey[keylen-1]='\0';
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
|
||||||
|
//prepare a search tuple
|
||||||
|
datatuple search_tuple;
|
||||||
|
search_tuple.keylen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
*(search_tuple.keylen) = keylen;
|
||||||
|
search_tuple.key = rkey;
|
||||||
|
|
||||||
|
|
||||||
|
datatuple *ret_tuple=0;
|
||||||
|
//step 1: look in tree_c0
|
||||||
|
|
||||||
|
rbtree_t::iterator rbitr = rbtree.find(search_tuple);
|
||||||
|
if(rbitr != rbtree.end())
|
||||||
|
{
|
||||||
|
datatuple tuple = *rbitr;
|
||||||
|
byte *barr = tuple.to_bytes();
|
||||||
|
ret_tuple = datatuple::from_bytes(barr);
|
||||||
|
|
||||||
|
found_tuples++;
|
||||||
|
assert(*(ret_tuple->keylen) == key_arr[ri].length()+1);
|
||||||
|
assert(*(ret_tuple->datalen) == data_arr[ri].length()+1);
|
||||||
|
free(barr);
|
||||||
|
free(ret_tuple);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Not in scratch_tree\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(search_tuple.keylen);
|
||||||
|
free(rkey);
|
||||||
|
}
|
||||||
|
printf("found %d\n", found_tuples);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
insertProbeIter(250);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
107
check_server.cpp
Normal file
107
check_server.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "merger.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
logserver *lserver=0;
|
||||||
|
merge_scheduler *mscheduler=0;
|
||||||
|
|
||||||
|
void terminate (int param)
|
||||||
|
{
|
||||||
|
printf ("Stopping server...\n");
|
||||||
|
lserver->stopserver();
|
||||||
|
delete lserver;
|
||||||
|
|
||||||
|
printf("Stopping merge threads...\n");
|
||||||
|
mscheduler->shutdown();
|
||||||
|
delete mscheduler;
|
||||||
|
|
||||||
|
printf("Deinitializing stasis...\n");
|
||||||
|
fflush(stdout);
|
||||||
|
Tdeinit();
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
//signal handling
|
||||||
|
void (*prev_fn)(int);
|
||||||
|
|
||||||
|
prev_fn = signal (SIGINT,terminate);
|
||||||
|
//if (prev_fn==SIG_IGN)
|
||||||
|
//signal (SIGTERM,SIG_IGN);
|
||||||
|
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
bufferManagerNonBlockingSlowHandleType = IO_HANDLE_PFILE;
|
||||||
|
|
||||||
|
Tinit();
|
||||||
|
|
||||||
|
int xid = Tbegin();
|
||||||
|
|
||||||
|
mscheduler = new merge_scheduler;
|
||||||
|
logtable ltable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int pcount = 40;
|
||||||
|
ltable.set_fixed_page_count(pcount);
|
||||||
|
|
||||||
|
recordid table_root = ltable.allocTable(xid);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
int lindex = mscheduler->addlogtable(<able);
|
||||||
|
ltable.setMergeData(mscheduler->getMergeData(lindex));
|
||||||
|
|
||||||
|
mscheduler->startlogtable(lindex);
|
||||||
|
|
||||||
|
|
||||||
|
lserver = new logserver(10, 32432);
|
||||||
|
|
||||||
|
lserver->startserver(<able);
|
||||||
|
|
||||||
|
|
||||||
|
// Tdeinit();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//insertProbeIter(25000);
|
||||||
|
insertProbeIter(10000);
|
||||||
|
/*
|
||||||
|
insertProbeIter(5000);
|
||||||
|
insertProbeIter(2500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(100);
|
||||||
|
insertProbeIter(10);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
415
check_tcpclient.cpp
Normal file
415
check_tcpclient.cpp
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "logstore.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mycmp(const std::string & k1,const std::string & k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp(k1.c_str(),k2.c_str()) < 0;
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be given a sorted array
|
||||||
|
void removeduplicates(std::vector<std::string> *arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(int i=arr->size()-1; i>0; i--)
|
||||||
|
{
|
||||||
|
if(! (mycmp((*arr)[i], (*arr)[i-1]) || mycmp((*arr)[i-1], (*arr)[i])))
|
||||||
|
arr->erase(arr->begin()+i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void getnextdata(std::string &data, int avg_len)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
data = std::string(str_len, rand()%10+48);
|
||||||
|
/*
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
data = std::string(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void preprandstr(int count, std::vector<std::string> *arr, int avg_len=50)
|
||||||
|
{
|
||||||
|
|
||||||
|
for ( int j=0; j<count; j++)
|
||||||
|
{
|
||||||
|
int str_len = (rand()%(avg_len*2)) + 3;
|
||||||
|
|
||||||
|
char *rc = (char*)malloc(str_len);
|
||||||
|
|
||||||
|
for(int i=0; i<str_len-1; i++)
|
||||||
|
rc[i] = rand()%10+48;
|
||||||
|
|
||||||
|
rc[str_len-1]='\0';
|
||||||
|
std::string str(rc);
|
||||||
|
|
||||||
|
free(rc);
|
||||||
|
|
||||||
|
arr->push_back(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void readfromsocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += read( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void writetosocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += write( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
datatuple * sendTuple(std::string & servername, int serverport, uint8_t opcode, datatuple &tuple)
|
||||||
|
{
|
||||||
|
struct sockaddr_in serveraddr;
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR opening socket.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = gethostbyname(servername.c_str());
|
||||||
|
if (server == NULL) {
|
||||||
|
fprintf(stderr,"ERROR, no such host as %s\n", servername.c_str());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the server's Internet address */
|
||||||
|
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||||
|
serveraddr.sin_family = AF_INET;
|
||||||
|
bcopy((char *)server->h_addr,
|
||||||
|
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
|
||||||
|
serveraddr.sin_port = htons(serverport);
|
||||||
|
|
||||||
|
/* connect: create a connection with the server */
|
||||||
|
if (connect(sockfd, (sockaddr*) &serveraddr, sizeof(serveraddr)) < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR connecting\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//send the opcode
|
||||||
|
int n = write(sockfd, (byte*) &opcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
//send the tuple
|
||||||
|
n = write(sockfd, (byte*) tuple.keylen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
|
||||||
|
n = write(sockfd, (byte*) tuple.datalen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
|
||||||
|
writetosocket(sockfd, (byte*) tuple.key, *tuple.keylen);
|
||||||
|
if(!tuple.isDelete() && *tuple.datalen != 0)
|
||||||
|
writetosocket(sockfd, (byte*) tuple.data, *tuple.datalen);
|
||||||
|
|
||||||
|
//read the reply code
|
||||||
|
uint8_t rcode;
|
||||||
|
n = read(sockfd, (byte*) &rcode, sizeof(uint8_t));
|
||||||
|
|
||||||
|
if(rcode == logserver::OP_SENDING_TUPLE)
|
||||||
|
{
|
||||||
|
datatuple *rcvdtuple = (datatuple*)malloc(sizeof(datatuple));
|
||||||
|
//read the keylen
|
||||||
|
rcvdtuple->keylen = (uint32_t*) malloc(sizeof(uint32_t));
|
||||||
|
n = read(sockfd, (byte*) rcvdtuple->keylen, sizeof(uint32_t));
|
||||||
|
assert(n == sizeof(uint32_t));
|
||||||
|
//read the datalen
|
||||||
|
rcvdtuple->datalen = (uint32_t*) malloc(sizeof(uint32_t));
|
||||||
|
n = read(sockfd, (byte*) rcvdtuple->datalen, sizeof(uint32_t));
|
||||||
|
assert(n == sizeof(uint32_t));
|
||||||
|
//read key
|
||||||
|
rcvdtuple->key = (byte*) malloc(*rcvdtuple->keylen);
|
||||||
|
readfromsocket(sockfd, (byte*) rcvdtuple->key, *rcvdtuple->keylen);
|
||||||
|
if(!rcvdtuple->isDelete())
|
||||||
|
{
|
||||||
|
//read key
|
||||||
|
rcvdtuple->data = (byte*) malloc(*rcvdtuple->datalen);
|
||||||
|
readfromsocket(sockfd, (byte*) rcvdtuple->data, *rcvdtuple->datalen);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
return rcvdtuple;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assert(rcode == logserver::OP_SUCCESS);
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void insertProbeIter(int NUM_ENTRIES)
|
||||||
|
{
|
||||||
|
srand(1000);
|
||||||
|
std::string servername = "sherpa4";
|
||||||
|
int serverport = 32432;
|
||||||
|
|
||||||
|
double delete_freq = .05;
|
||||||
|
double update_freq = .15;
|
||||||
|
|
||||||
|
//data generation
|
||||||
|
typedef std::vector<std::string> key_v_t;
|
||||||
|
const static int max_partition_size = 100000;
|
||||||
|
int KEY_LEN = 100;
|
||||||
|
std::vector<key_v_t*> *key_v_list = new std::vector<key_v_t*>;
|
||||||
|
int list_size = NUM_ENTRIES / max_partition_size + 1;
|
||||||
|
for(int i =0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
key_v_t * key_arr = new key_v_t;
|
||||||
|
if(NUM_ENTRIES < max_partition_size*(i+1))
|
||||||
|
preprandstr(NUM_ENTRIES-max_partition_size*i, key_arr, KEY_LEN);
|
||||||
|
else
|
||||||
|
preprandstr(max_partition_size, key_arr, KEY_LEN);
|
||||||
|
|
||||||
|
std::sort(key_arr->begin(), key_arr->end(), &mycmp);
|
||||||
|
key_v_list->push_back(key_arr);
|
||||||
|
printf("size partition %d is %d\n", i+1, key_arr->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
key_v_t * key_arr = new key_v_t;
|
||||||
|
|
||||||
|
std::vector<key_v_t::iterator*> iters;
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
iters.push_back(new key_v_t::iterator((*key_v_list)[i]->begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int lc = 0;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int list_index = -1;
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
if(*iters[i] == (*key_v_list)[i]->end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list_index == -1 || mycmp(**iters[i], **iters[list_index]))
|
||||||
|
list_index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(list_index == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(key_arr->size() == 0 || mycmp(key_arr->back(), **iters[list_index]))
|
||||||
|
key_arr->push_back(**iters[list_index]);
|
||||||
|
|
||||||
|
(*iters[list_index])++;
|
||||||
|
lc++;
|
||||||
|
if(lc % max_partition_size == 0)
|
||||||
|
printf("%d/%d completed.\n", lc, NUM_ENTRIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<list_size; i++)
|
||||||
|
{
|
||||||
|
(*key_v_list)[i]->clear();
|
||||||
|
delete (*key_v_list)[i];
|
||||||
|
delete iters[i];
|
||||||
|
}
|
||||||
|
key_v_list->clear();
|
||||||
|
delete key_v_list;
|
||||||
|
|
||||||
|
// preprandstr(NUM_ENTRIES, data_arr, 10*8192);
|
||||||
|
|
||||||
|
printf("key arr size: %d\n", key_arr->size());
|
||||||
|
|
||||||
|
//removeduplicates(key_arr);
|
||||||
|
if(key_arr->size() > NUM_ENTRIES)
|
||||||
|
key_arr->erase(key_arr->begin()+NUM_ENTRIES, key_arr->end());
|
||||||
|
|
||||||
|
NUM_ENTRIES=key_arr->size();
|
||||||
|
|
||||||
|
printf("Stage 1: Writing %d keys\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
struct timeval start_tv, stop_tv, ti_st, ti_end;
|
||||||
|
double insert_time = 0;
|
||||||
|
int dpages = 0;
|
||||||
|
int npages = 0;
|
||||||
|
int delcount = 0, upcount = 0;
|
||||||
|
int64_t datasize = 0;
|
||||||
|
std::vector<pageid_t> dsp;
|
||||||
|
std::vector<int> del_list;
|
||||||
|
gettimeofday(&start_tv,0);
|
||||||
|
for(int i = 0; i < NUM_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
//prepare the key
|
||||||
|
datatuple newtuple;
|
||||||
|
uint32_t keylen = (*key_arr)[i].length()+1;
|
||||||
|
newtuple.keylen = &keylen;
|
||||||
|
|
||||||
|
newtuple.key = (datatuple::key_t) malloc(keylen);
|
||||||
|
memcpy((byte*)newtuple.key, (*key_arr)[i].c_str(), keylen);
|
||||||
|
|
||||||
|
//prepare the data
|
||||||
|
std::string ditem;
|
||||||
|
getnextdata(ditem, 8192);
|
||||||
|
uint32_t datalen = ditem.length()+1;
|
||||||
|
newtuple.datalen = &datalen;
|
||||||
|
newtuple.data = (datatuple::data_t) malloc(datalen);
|
||||||
|
memcpy((byte*)newtuple.data, ditem.c_str(), datalen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("key: \t, keylen: %u\ndata: datalen: %u\n",
|
||||||
|
//newtuple.key,
|
||||||
|
*newtuple.keylen,
|
||||||
|
//newtuple.data,
|
||||||
|
*newtuple.datalen);
|
||||||
|
*/
|
||||||
|
|
||||||
|
datasize += newtuple.byte_length();
|
||||||
|
|
||||||
|
gettimeofday(&ti_st,0);
|
||||||
|
|
||||||
|
//send the data
|
||||||
|
sendTuple(servername, serverport, logserver::OP_INSERT, newtuple);
|
||||||
|
|
||||||
|
gettimeofday(&ti_end,0);
|
||||||
|
insert_time += tv_to_double(ti_end) - tv_to_double(ti_st);
|
||||||
|
|
||||||
|
free(newtuple.key);
|
||||||
|
free(newtuple.data);
|
||||||
|
|
||||||
|
if(i % 10000 == 0 && i > 0)
|
||||||
|
printf("%d / %d inserted.\n", i, NUM_ENTRIES);
|
||||||
|
|
||||||
|
}
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("insert time: %6.1f\n", insert_time);
|
||||||
|
printf("insert time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
printf("#deletions: %d\n#updates: %d\n", delcount, upcount);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("Stage 2: Looking up %d keys:\n", NUM_ENTRIES);
|
||||||
|
|
||||||
|
int found_tuples=0;
|
||||||
|
for(int i=NUM_ENTRIES-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
int ri = i;
|
||||||
|
//printf("key index%d\n", i);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
//get the key
|
||||||
|
uint32_t keylen = (*key_arr)[ri].length()+1;
|
||||||
|
datatuple searchtuple;
|
||||||
|
searchtuple.keylen = (uint32_t*)malloc(2*sizeof(uint32_t) + keylen);
|
||||||
|
*searchtuple.keylen = keylen;
|
||||||
|
|
||||||
|
searchtuple.datalen = searchtuple.keylen + 1;
|
||||||
|
*searchtuple.datalen = 0;
|
||||||
|
|
||||||
|
searchtuple.key = (datatuple::key_t)(searchtuple.keylen + 2);
|
||||||
|
memcpy((byte*)searchtuple.key, (*key_arr)[ri].c_str(), keylen);
|
||||||
|
|
||||||
|
//find the key with the given tuple
|
||||||
|
datatuple *dt = sendTuple(servername, serverport, logserver::OP_FIND,
|
||||||
|
searchtuple);
|
||||||
|
|
||||||
|
assert(dt!=0);
|
||||||
|
assert(!dt->isDelete());
|
||||||
|
found_tuples++;
|
||||||
|
assert(*(dt->keylen) == (*key_arr)[ri].length()+1);
|
||||||
|
|
||||||
|
//free dt
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt->datalen);
|
||||||
|
free(dt->key);
|
||||||
|
free(dt->data);
|
||||||
|
free(dt);
|
||||||
|
|
||||||
|
dt = 0;
|
||||||
|
|
||||||
|
free(searchtuple.keylen);
|
||||||
|
|
||||||
|
}
|
||||||
|
printf("found %d\n", found_tuples);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
key_arr->clear();
|
||||||
|
//data_arr->clear();
|
||||||
|
delete key_arr;
|
||||||
|
//delete data_arr;
|
||||||
|
|
||||||
|
gettimeofday(&stop_tv,0);
|
||||||
|
printf("run time: %6.1f\n", (tv_to_double(stop_tv) - tv_to_double(start_tv)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @test
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//insertProbeIter(25000);
|
||||||
|
insertProbeIter(100000);
|
||||||
|
/*
|
||||||
|
insertProbeIter(5000);
|
||||||
|
insertProbeIter(2500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(500);
|
||||||
|
insertProbeIter(1000);
|
||||||
|
insertProbeIter(100);
|
||||||
|
insertProbeIter(10);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
9
cmds.txt
Normal file
9
cmds.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
dd if=/dev/zero of=storefile.txt bs=1M count=20000
|
||||||
|
|
||||||
|
|
||||||
|
/dhtRecOpsGenerator -d clientType=LogStoreClient host=sherpa4 numOps=10ls existingStartKey=100 existingEndKey=1000 insertRatio=1.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dhtRecOpsGeneratorWrapper startClientID=1 endClientID=4 -d clientType=LogStoreClient host=sherpa4.corp.re1.yahoo.com numOps=5000000 existingStartKey=100 existingEndKey=10000000 insertRatio=1.0 readRatio=0 numClients=3
|
507
datapage.cpp
Normal file
507
datapage.cpp
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "datapage.h"
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
const int32_t DataPage<TUPLE>::HEADER_SIZE = sizeof(int32_t);
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
DataPage<TUPLE>::DataPage(int xid, pageid_t pid):
|
||||||
|
alloc_region(0),
|
||||||
|
alloc_state(0),
|
||||||
|
fix_pcount(-1)
|
||||||
|
{
|
||||||
|
assert(pid!=0);
|
||||||
|
|
||||||
|
pcount = readPageCount(xid, pid);
|
||||||
|
|
||||||
|
pidarr = (pageid_t *) malloc(sizeof(pageid_t) * pcount);
|
||||||
|
|
||||||
|
for(int i=0; i<pcount; i++)
|
||||||
|
pidarr[i] = i + pid;
|
||||||
|
|
||||||
|
byte_offset = HEADER_SIZE; //step over the header info
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
DataPage<TUPLE>::DataPage(int xid, int fix_pcount, pageid_t (*alloc_region)(int, void*), void * alloc_state)
|
||||||
|
{
|
||||||
|
assert(fix_pcount >= 1);
|
||||||
|
byte_offset = -1;
|
||||||
|
|
||||||
|
this->fix_pcount = fix_pcount;
|
||||||
|
|
||||||
|
if(alloc_region != 0)
|
||||||
|
this->alloc_region = alloc_region;
|
||||||
|
if(alloc_state != 0)
|
||||||
|
this->alloc_state = alloc_state;
|
||||||
|
|
||||||
|
initialize(xid);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TUPLE>
|
||||||
|
DataPage<TUPLE>::~DataPage()
|
||||||
|
{
|
||||||
|
if(pidarr)
|
||||||
|
free(pidarr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class TUPLE>
|
||||||
|
void DataPage<TUPLE>::initialize(int xid)
|
||||||
|
{
|
||||||
|
//initializes to an empty datapage
|
||||||
|
//alloc a new page
|
||||||
|
pageid_t pid = alloc_region(xid, alloc_state);
|
||||||
|
|
||||||
|
//load the first page
|
||||||
|
//Page *p = loadPage(xid, pid);
|
||||||
|
Page *p = loadPageOfType(xid, pid, SEGMENT_PAGE);
|
||||||
|
writelock(p->rwlatch,0);
|
||||||
|
|
||||||
|
//initialize header
|
||||||
|
|
||||||
|
//set number of pages to 1
|
||||||
|
int32_t * numpages_ptr = (int32_t*)stasis_page_byte_ptr_from_start(p, 0);
|
||||||
|
*numpages_ptr = 1;
|
||||||
|
|
||||||
|
//write 0 to first data size
|
||||||
|
int32_t * size_ptr = (int32_t*)stasis_page_byte_ptr_from_start(p, HEADER_SIZE);
|
||||||
|
*size_ptr = 0;
|
||||||
|
|
||||||
|
//set the page dirty
|
||||||
|
stasis_dirty_page_table_set_dirty((stasis_dirty_page_table_t*)stasis_runtime_dirty_page_table(), p);
|
||||||
|
|
||||||
|
//release the page
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
//set the class variables
|
||||||
|
byte_offset = HEADER_SIZE;
|
||||||
|
pcount = 1;
|
||||||
|
pidarr = (pageid_t *) malloc(fix_pcount * sizeof(pageid_t));
|
||||||
|
pidarr[0] = pid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
inline bool DataPage<TUPLE>::append(int xid, TUPLE const & dat)
|
||||||
|
{
|
||||||
|
assert(byte_offset >= HEADER_SIZE);
|
||||||
|
assert(fix_pcount >= 1);
|
||||||
|
|
||||||
|
//check if there is enough space (for the data length + data)
|
||||||
|
int32_t blen = dat.byte_length() + sizeof(int32_t);
|
||||||
|
if(PAGE_SIZE * fix_pcount - byte_offset < blen)
|
||||||
|
{
|
||||||
|
//check if the record is too large
|
||||||
|
// and if so do we wanna accomodate here by going over the fix_pcount
|
||||||
|
if(PAGE_SIZE * fix_pcount - HEADER_SIZE < blen && //record is larger than datapage
|
||||||
|
PAGE_SIZE * fix_pcount - HEADER_SIZE > 2 * byte_offset)//accept if i am less than half full
|
||||||
|
{
|
||||||
|
//nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//printf("page has %d bytes left, we needed %d. (byte_offset %d)\n",
|
||||||
|
//PAGE_SIZE * fix_pcount - byte_offset, blen, byte_offset);
|
||||||
|
return false; //not enough mana, return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//write the length of the data
|
||||||
|
int32_t dsize = blen - sizeof(int32_t);
|
||||||
|
|
||||||
|
if(!writebytes(xid, sizeof(int32_t), (byte*)(&dsize)))
|
||||||
|
return false;
|
||||||
|
byte_offset += sizeof(int32_t);
|
||||||
|
|
||||||
|
//write the data
|
||||||
|
byte * barr = dat.to_bytes();
|
||||||
|
if(!writebytes(xid, dsize, barr)) //if write fails, undo the previous write
|
||||||
|
{
|
||||||
|
byte_offset -= sizeof(int32_t);
|
||||||
|
free(barr);
|
||||||
|
//write 0 for the next tuple size, if there is enough space in this page
|
||||||
|
if(PAGE_SIZE - (byte_offset % PAGE_SIZE) >= sizeof(int32_t))
|
||||||
|
{
|
||||||
|
dsize = 0;
|
||||||
|
writebytes(xid, sizeof(int32_t), (byte*)(&dsize));//this will succeed, since there is enough space on the page
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(barr);
|
||||||
|
byte_offset += dsize;
|
||||||
|
|
||||||
|
//write 0 for the next tuple size, if there is enough space in this page
|
||||||
|
if(PAGE_SIZE - (byte_offset % PAGE_SIZE) >= sizeof(int32_t))
|
||||||
|
{
|
||||||
|
dsize = 0;
|
||||||
|
writebytes(xid, sizeof(int32_t), (byte*)(&dsize));//this will succeed, since there is enough space on the page
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
bool DataPage<TUPLE>::writebytes(int xid, int count, byte *data)
|
||||||
|
{
|
||||||
|
|
||||||
|
int32_t bytes_copied = 0;
|
||||||
|
while(bytes_copied < count)
|
||||||
|
{
|
||||||
|
//load the page to copy into
|
||||||
|
int pindex = (byte_offset + bytes_copied) / PAGE_SIZE;
|
||||||
|
if(pindex == pcount) //then this page must be allocated
|
||||||
|
{
|
||||||
|
pageid_t newid = alloc_region(xid, alloc_state);
|
||||||
|
//check continuity
|
||||||
|
if(pidarr[pindex-1] != newid - 1)//so we started a new region and that is not right after the prev region in the file
|
||||||
|
{
|
||||||
|
return false;//we cant store this
|
||||||
|
}
|
||||||
|
|
||||||
|
//check whether we need to extend the pidarr, add fix_pcount many pageid_t slots
|
||||||
|
if(pindex >= fix_pcount && (pindex % fix_pcount==0))
|
||||||
|
{
|
||||||
|
pidarr = (pageid_t*)realloc(pidarr, (pindex + fix_pcount)*sizeof(pageid_t));
|
||||||
|
}
|
||||||
|
pidarr[pindex] = newid;
|
||||||
|
pcount++;
|
||||||
|
incrementPageCount(xid, pidarr[0]);
|
||||||
|
}
|
||||||
|
//Page *p = loadPage(xid, pidarr[pindex]);
|
||||||
|
Page *p = loadPageOfType(xid, pidarr[pindex], SEGMENT_PAGE);
|
||||||
|
writelock(p->rwlatch,0);
|
||||||
|
|
||||||
|
//copy the portion of bytes we can copy in this page
|
||||||
|
int32_t page_offset = (byte_offset+bytes_copied) % PAGE_SIZE;
|
||||||
|
int32_t copy_len = ( (PAGE_SIZE - page_offset < count - bytes_copied ) ? PAGE_SIZE - page_offset: count - bytes_copied);
|
||||||
|
|
||||||
|
byte * pb_ptr = stasis_page_byte_ptr_from_start(p, page_offset);
|
||||||
|
memcpy(pb_ptr, data+bytes_copied ,copy_len);
|
||||||
|
|
||||||
|
//release the page
|
||||||
|
stasis_dirty_page_table_set_dirty((stasis_dirty_page_table_t*)stasis_runtime_dirty_page_table(), p);
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
//update the copied bytes_count
|
||||||
|
bytes_copied += copy_len;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bytes_copied == count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
bool DataPage<TUPLE>::recordRead(int xid, typename TUPLE::key_t key, size_t keySize, TUPLE ** buf)
|
||||||
|
{
|
||||||
|
RecordIterator itr(this);
|
||||||
|
|
||||||
|
int match = -1;
|
||||||
|
while((*buf=itr.getnext(xid)) != 0)
|
||||||
|
{
|
||||||
|
match = TUPLE::compare((*buf)->get_key(), key);
|
||||||
|
|
||||||
|
if(match<0) //keep searching
|
||||||
|
{
|
||||||
|
free((*buf)->keylen);
|
||||||
|
free(*buf);
|
||||||
|
*buf=0;
|
||||||
|
}
|
||||||
|
else if(match==0) //found
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else // match > 0, then does not exist
|
||||||
|
{
|
||||||
|
free((*buf)->keylen);
|
||||||
|
free(*buf);
|
||||||
|
*buf = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
void DataPage<TUPLE>::readbytes(int xid, int32_t offset, int count, byte **data)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(*data==NULL)
|
||||||
|
*data = (byte*)malloc(count);
|
||||||
|
|
||||||
|
int32_t bytes_copied = 0;
|
||||||
|
while(bytes_copied < count)
|
||||||
|
{
|
||||||
|
//load the page to copy from
|
||||||
|
int pindex = (offset + bytes_copied) / PAGE_SIZE;
|
||||||
|
|
||||||
|
//Page *p = loadPage(xid, pidarr[pindex]);
|
||||||
|
Page *p = loadPageOfType(xid, pidarr[pindex], SEGMENT_PAGE);
|
||||||
|
readlock(p->rwlatch,0);
|
||||||
|
|
||||||
|
//copy the portion of bytes we can copy from this page
|
||||||
|
int32_t page_offset = (offset+bytes_copied) % PAGE_SIZE;
|
||||||
|
int32_t copy_len = ( (PAGE_SIZE - page_offset < count - bytes_copied ) ? PAGE_SIZE - page_offset : count - bytes_copied);
|
||||||
|
|
||||||
|
byte * pb_ptr = stasis_page_byte_ptr_from_start(p, page_offset);
|
||||||
|
memcpy((*data)+bytes_copied, pb_ptr, copy_len);
|
||||||
|
|
||||||
|
//release the page
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
//update the copied bytes_count
|
||||||
|
bytes_copied += copy_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bytes_copied == count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
inline int DataPage<TUPLE>::readPageCount(int xid, pageid_t pid)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Page *p = loadPage(xid, pid);
|
||||||
|
Page *p = loadPageOfType(xid, pid, SEGMENT_PAGE);
|
||||||
|
readlock(p->rwlatch,0);
|
||||||
|
|
||||||
|
int32_t numpages = *((int32_t*)stasis_page_byte_ptr_from_start(p, 0));
|
||||||
|
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
return numpages;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
inline void DataPage<TUPLE>::incrementPageCount(int xid, pageid_t pid, int add)
|
||||||
|
{
|
||||||
|
//Page *p = loadPage(xid, pid);
|
||||||
|
Page *p = loadPageOfType(xid, pid, SEGMENT_PAGE);
|
||||||
|
writelock(p->rwlatch,0);
|
||||||
|
|
||||||
|
int32_t *numpages_ptr = (int32_t*)stasis_page_byte_ptr_from_start(p, 0);
|
||||||
|
|
||||||
|
*numpages_ptr = *numpages_ptr + add;
|
||||||
|
|
||||||
|
stasis_dirty_page_table_set_dirty((stasis_dirty_page_table_t*)stasis_runtime_dirty_page_table(), p);
|
||||||
|
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
inline uint16_t DataPage<TUPLE>::recordCount(int xid)
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
pageid_t DataPage<TUPLE>::dp_alloc_region(int xid, void *conf)
|
||||||
|
{
|
||||||
|
RegionAllocConf_t* a = (RegionAllocConf_t*)conf;
|
||||||
|
|
||||||
|
|
||||||
|
if(a->nextPage == a->endOfRegion) {
|
||||||
|
if(a->regionList.size == -1) {
|
||||||
|
//DEBUG("nextPage: %lld\n", a->nextPage);
|
||||||
|
a->regionList = TarrayListAlloc(xid, 1, 4, sizeof(pageid_t));
|
||||||
|
DEBUG("regionList.page: %lld\n", a->regionList.page);
|
||||||
|
DEBUG("regionList.slot: %d\n", a->regionList.slot);
|
||||||
|
DEBUG("regionList.size: %lld\n", a->regionList.size);
|
||||||
|
|
||||||
|
a->regionCount = 0;
|
||||||
|
}
|
||||||
|
DEBUG("{%lld <- alloc region arraylist}\n", a->regionList.page);
|
||||||
|
TarrayListExtend(xid,a->regionList,1);
|
||||||
|
a->regionList.slot = a->regionCount;
|
||||||
|
DEBUG("region lst slot %d\n",a->regionList.slot);
|
||||||
|
a->regionCount++;
|
||||||
|
DEBUG("region count %lld\n",a->regionCount);
|
||||||
|
a->nextPage = TregionAlloc(xid, a->regionSize,12);
|
||||||
|
DEBUG("next page %lld\n",a->nextPage);
|
||||||
|
a->endOfRegion = a->nextPage + a->regionSize;
|
||||||
|
Tset(xid,a->regionList,&a->nextPage);
|
||||||
|
DEBUG("next page %lld\n",a->nextPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("%lld ?= %lld\n", a->nextPage,a->endOfRegion);
|
||||||
|
pageid_t ret = a->nextPage;
|
||||||
|
// Ensure the page is in buffer cache without accessing disk (this
|
||||||
|
// sets it to clean and all zeros if the page is not in cache).
|
||||||
|
// Hopefully, future reads will get a cache hit, and avoid going to
|
||||||
|
// disk.
|
||||||
|
|
||||||
|
Page * p = loadUninitializedPage(xid, ret);
|
||||||
|
//writelock(p->rwlatch,0);
|
||||||
|
p->pageType = SEGMENT_PAGE;
|
||||||
|
//unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
DEBUG("ret %lld\n",ret);
|
||||||
|
(a->nextPage)++;
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
pageid_t DataPage<TUPLE>::dp_alloc_region_rid(int xid, void * ridp) {
|
||||||
|
recordid rid = *(recordid*)ridp;
|
||||||
|
RegionAllocConf_t conf;
|
||||||
|
Tread(xid,rid,&conf);
|
||||||
|
pageid_t ret = dp_alloc_region(xid,&conf);
|
||||||
|
//DEBUG("{%lld <- alloc region extend}\n", conf.regionList.page);
|
||||||
|
// XXX get rid of Tset by storing next page in memory, and losing it
|
||||||
|
// on crash.
|
||||||
|
Tset(xid,rid,&conf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
void DataPage<TUPLE>::dealloc_region_rid(int xid, void *conf)
|
||||||
|
{
|
||||||
|
RegionAllocConf_t a = *((RegionAllocConf_t*)conf);
|
||||||
|
DEBUG("{%lld <- dealloc region arraylist}\n", a.regionList.page);
|
||||||
|
|
||||||
|
for(int i = 0; i < a.regionCount; i++) {
|
||||||
|
a.regionList.slot = i;
|
||||||
|
pageid_t pid;
|
||||||
|
Tread(xid,a.regionList,&pid);
|
||||||
|
TregionDealloc(xid,pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
void DataPage<TUPLE>::force_region_rid(int xid, void *conf)
|
||||||
|
{
|
||||||
|
recordid rid = *(recordid*)conf;
|
||||||
|
RegionAllocConf_t a;
|
||||||
|
Tread(xid,rid,&a);
|
||||||
|
|
||||||
|
for(int i = 0; i < a.regionCount; i++)
|
||||||
|
{
|
||||||
|
a.regionList.slot = i;
|
||||||
|
pageid_t pid;
|
||||||
|
Tread(xid,a.regionList,&pid);
|
||||||
|
stasis_dirty_page_table_flush_range((stasis_dirty_page_table_t*)stasis_runtime_dirty_page_table(), pid, pid+a.regionSize);
|
||||||
|
forcePageRange(pid, pid+a.regionSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
//RECORD ITERATOR
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
TUPLE* DataPage<TUPLE>::RecordIterator::getnext(int xid)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
int pindex = offset / PAGE_SIZE;
|
||||||
|
|
||||||
|
if(pindex == dp->pcount)//past end
|
||||||
|
return 0;
|
||||||
|
if(pindex == dp->pcount - 1 && (PAGE_SIZE - (offset % PAGE_SIZE) < sizeof(int32_t)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
//Page *p = loadPage(xid, dp->pidarr[pindex]);
|
||||||
|
Page *p = loadPageOfType(xid, dp->pidarr[pindex], SEGMENT_PAGE);
|
||||||
|
readlock(p->rwlatch,0);
|
||||||
|
|
||||||
|
int32_t *dsize_ptr;
|
||||||
|
if(PAGE_SIZE - (offset % PAGE_SIZE) < sizeof(int32_t)) //int spread in two pages
|
||||||
|
{
|
||||||
|
dsize_ptr = 0;
|
||||||
|
dp->readbytes(xid, offset, sizeof(int32_t), (byte**)(&dsize_ptr));
|
||||||
|
}
|
||||||
|
else //int in a single page
|
||||||
|
dsize_ptr = (int32_t*)stasis_page_byte_ptr_from_start(p, offset % PAGE_SIZE);
|
||||||
|
|
||||||
|
offset += sizeof(int32_t);
|
||||||
|
|
||||||
|
if(*dsize_ptr == 0) //no more keys
|
||||||
|
{
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte* tb=0;
|
||||||
|
dp->readbytes(xid, offset, *dsize_ptr, &tb);
|
||||||
|
|
||||||
|
TUPLE *tup = TUPLE::from_bytes(tb);
|
||||||
|
|
||||||
|
offset += *dsize_ptr;
|
||||||
|
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
|
||||||
|
return tup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
void DataPage<TUPLE>::RecordIterator::advance(int xid, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
int pindex = -1;
|
||||||
|
Page *p = 0;
|
||||||
|
|
||||||
|
for(int i=0; i<count; i++)
|
||||||
|
{
|
||||||
|
if(pindex != offset / PAGE_SIZE) //advance to new page if necessary
|
||||||
|
{
|
||||||
|
if(p!=NULL)
|
||||||
|
{
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
pindex = offset / PAGE_SIZE;
|
||||||
|
|
||||||
|
if(pindex == dp->pcount)//past end
|
||||||
|
return;
|
||||||
|
|
||||||
|
//p = loadPage(xid, dp->pidarr[pindex]);
|
||||||
|
p = loadPageOfType(xid, dp->pidarr[pindex], SEGMENT_PAGE);
|
||||||
|
readlock(p->rwlatch,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pindex == dp->pcount - 1 && (PAGE_SIZE - (offset % PAGE_SIZE) < sizeof(int32_t)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int32_t *dsize_ptr=0;
|
||||||
|
if(PAGE_SIZE - (offset % PAGE_SIZE) < sizeof(int32_t)) //int spread in two pages
|
||||||
|
dp->readbytes(xid, offset, sizeof(int32_t), (byte**)(&dsize_ptr));
|
||||||
|
else //int in a single page
|
||||||
|
dsize_ptr = (int32_t*)stasis_page_byte_ptr_from_start(p, offset % PAGE_SIZE);
|
||||||
|
|
||||||
|
offset += sizeof(int32_t);
|
||||||
|
|
||||||
|
if(*dsize_ptr == 0) //no more keys
|
||||||
|
{
|
||||||
|
unlock(p->rwlatch);
|
||||||
|
releasePage(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += *dsize_ptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
datapage.h
Normal file
110
datapage.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#ifndef _SIMPLE_DATA_PAGE_H_
|
||||||
|
#define _SIMPLE_DATA_PAGE_H_
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <stasis/page.h>
|
||||||
|
#include <stasis/constants.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class TUPLE>
|
||||||
|
class DataPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
class RecordIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RecordIterator(DataPage *dp)
|
||||||
|
{
|
||||||
|
offset = HEADER_SIZE;
|
||||||
|
this->dp = dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordIterator(const RecordIterator &rhs)
|
||||||
|
{
|
||||||
|
this->offset = rhs.offset;
|
||||||
|
this->dp = rhs.dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const RecordIterator &rhs)
|
||||||
|
{
|
||||||
|
this->offset = rhs.offset;
|
||||||
|
this->dp = rhs.dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//returns the next tuple and also advances the iterator
|
||||||
|
TUPLE *getnext(int xid);
|
||||||
|
|
||||||
|
//advance the iterator by count tuples, i.e. skip over count tuples
|
||||||
|
void advance(int xid, int count=1);
|
||||||
|
|
||||||
|
|
||||||
|
int32_t offset ;
|
||||||
|
DataPage *dp;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//to be used when reading an existing data page from disk
|
||||||
|
DataPage( int xid, pageid_t pid );
|
||||||
|
|
||||||
|
//to be used to create new data pages
|
||||||
|
DataPage( int xid, int fix_pcount, pageid_t (*alloc_region)(int, void*), void * alloc_state);
|
||||||
|
|
||||||
|
~DataPage();
|
||||||
|
|
||||||
|
inline bool append(int xid, TUPLE const & dat);
|
||||||
|
bool recordRead(int xid, typename TUPLE::key_t key, size_t keySize, TUPLE ** buf);
|
||||||
|
|
||||||
|
inline uint16_t recordCount(int xid);
|
||||||
|
|
||||||
|
|
||||||
|
RecordIterator begin(){return RecordIterator(this);}
|
||||||
|
|
||||||
|
pageid_t get_start_pid(){return pidarr[0];}
|
||||||
|
int get_page_count(){return pcount;}
|
||||||
|
|
||||||
|
static pageid_t dp_alloc_region(int xid, void *conf);
|
||||||
|
|
||||||
|
static pageid_t dp_alloc_region_rid(int xid, void * ridp);
|
||||||
|
|
||||||
|
static void dealloc_region_rid(int xid, void* conf);
|
||||||
|
|
||||||
|
static void force_region_rid(int xid, void *conf);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void initialize(int xid);
|
||||||
|
|
||||||
|
//reads the page count information from the first page
|
||||||
|
int readPageCount(int xid, pageid_t pid);
|
||||||
|
void incrementPageCount(int xid, pageid_t pid, int add=1);
|
||||||
|
|
||||||
|
bool writebytes(int xid, int count, byte *data);
|
||||||
|
inline void readbytes(int xid, int32_t offset, int count, byte **data=0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fix_pcount; //number of pages in a standard data page
|
||||||
|
int pcount;
|
||||||
|
pageid_t *pidarr;
|
||||||
|
int32_t byte_offset;//points to the next free byte
|
||||||
|
|
||||||
|
|
||||||
|
//page alloc function
|
||||||
|
pageid_t (*alloc_region)(int, void*);
|
||||||
|
void *alloc_state;
|
||||||
|
|
||||||
|
static const int32_t HEADER_SIZE;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
147
datatuple.h
Normal file
147
datatuple.h
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#ifndef _DATATUPLE_H_
|
||||||
|
#define _DATATUPLE_H_
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
//#define byte unsigned char
|
||||||
|
typedef unsigned char byte;
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
//#include <stdio.h>
|
||||||
|
//#include <stdlib.h>
|
||||||
|
//#include <errno.h>
|
||||||
|
|
||||||
|
typedef struct datatuple
|
||||||
|
{
|
||||||
|
typedef uchar* key_t;
|
||||||
|
typedef uchar* data_t;
|
||||||
|
uint32_t *keylen; //key length should be size of string + 1 for \n
|
||||||
|
uint32_t *datalen;
|
||||||
|
key_t key;
|
||||||
|
data_t data;
|
||||||
|
|
||||||
|
//this is used by the stl set
|
||||||
|
bool operator() (const datatuple& lhs, const datatuple& rhs) const
|
||||||
|
{
|
||||||
|
//std::basic_string<uchar> s1(lhs.key);
|
||||||
|
//std::basic_string<uchar> s2(rhs.key);
|
||||||
|
return strcmp((char*)lhs.key,(char*)rhs.key) < 0;
|
||||||
|
//return (*((int32_t*)lhs.key)) <= (*((int32_t*)rhs.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return -1 if k1 < k2
|
||||||
|
* 0 if k1 == k2
|
||||||
|
* 1 of k1 > k2
|
||||||
|
**/
|
||||||
|
static int compare(const key_t k1,const key_t k2)
|
||||||
|
{
|
||||||
|
//for char* ending with \0
|
||||||
|
return strcmp((char*)k1,(char*)k2);
|
||||||
|
|
||||||
|
//for int32_t
|
||||||
|
//printf("%d\t%d\n",(*((int32_t*)k1)) ,(*((int32_t*)k2)));
|
||||||
|
//return (*((int32_t*)k1)) <= (*((int32_t*)k2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDelete()
|
||||||
|
{
|
||||||
|
*datalen = UINT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isDelete() const
|
||||||
|
{
|
||||||
|
return *datalen == UINT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string key_to_str(const byte* k)
|
||||||
|
{
|
||||||
|
//for strings
|
||||||
|
return std::string((char*)k);
|
||||||
|
//for int
|
||||||
|
/*
|
||||||
|
std::ostringstream ostr;
|
||||||
|
ostr << *((int32_t*)k);
|
||||||
|
return ostr.str();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns the length of the byte array representation
|
||||||
|
int32_t byte_length() const{
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
if(isDelete())
|
||||||
|
return isize + *keylen + isize;
|
||||||
|
else
|
||||||
|
return isize + *keylen + isize + (*datalen);
|
||||||
|
}
|
||||||
|
|
||||||
|
//format: key length _ data length _ key _ data
|
||||||
|
byte * to_bytes() const {
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
byte * ret;
|
||||||
|
if(!isDelete())
|
||||||
|
ret = (byte*) malloc(isize + *keylen + isize + *datalen);
|
||||||
|
else
|
||||||
|
ret = (byte*) malloc(isize + *keylen + isize);
|
||||||
|
|
||||||
|
memcpy(ret, (byte*)(keylen), isize);
|
||||||
|
memcpy(ret+isize, (byte*)(datalen), isize);
|
||||||
|
memcpy(ret+isize+isize, key, *keylen);
|
||||||
|
if(!isDelete())
|
||||||
|
memcpy(ret+isize+isize+*keylen, data, *datalen);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//does not copy the data again
|
||||||
|
//just sets the pointers in the datatuple to
|
||||||
|
//right positions in the given arr
|
||||||
|
|
||||||
|
static datatuple* from_bytes(const byte * arr)
|
||||||
|
{
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
datatuple *dt = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
|
||||||
|
dt->keylen = (uint32_t*) arr;
|
||||||
|
dt->datalen = (uint32_t*) (arr+isize);
|
||||||
|
dt->key = (key_t) (arr+isize+isize);
|
||||||
|
if(!dt->isDelete())
|
||||||
|
dt->data = (data_t) (arr+isize+isize+ *(dt->keylen));
|
||||||
|
else
|
||||||
|
dt->data = 0;
|
||||||
|
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
static datatuple form_tuple(const byte * arr)
|
||||||
|
{
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
datatuple dt;
|
||||||
|
|
||||||
|
dt.keylen = (uint32_t*) arr;
|
||||||
|
dt.datalen = (uint32_t*) (arr+isize);
|
||||||
|
dt.key = (key_t) (arr+isize+isize);
|
||||||
|
if(!dt.isDelete())
|
||||||
|
dt.data = (data_t) (arr+isize+isize+ *(dt.keylen));
|
||||||
|
else
|
||||||
|
dt.data = 0;
|
||||||
|
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
byte * get_key() { return (byte*) key; }
|
||||||
|
byte * get_data() { return (byte*) data; }
|
||||||
|
|
||||||
|
//releases only the tuple
|
||||||
|
static void release(datatuple *dt)
|
||||||
|
{
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
} datatuple;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
48
hello.cpp
Normal file
48
hello.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include<stasis/transactional.h>
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef struct datatuple
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef byte* key_t;
|
||||||
|
typedef byte* data_t;
|
||||||
|
uint32_t keylen;
|
||||||
|
uint32_t datalen;
|
||||||
|
key_t key;
|
||||||
|
data_t data;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
bool * m1 = new bool(false);
|
||||||
|
std::cout << *m1 << std::endl;
|
||||||
|
|
||||||
|
datatuple t;
|
||||||
|
std::cout << "size of datatuple:\t" << sizeof(datatuple) << std::endl;
|
||||||
|
|
||||||
|
t.key = (datatuple::key_t) malloc(10);
|
||||||
|
const char * str = "12345678";
|
||||||
|
strcpy((char*)t.key, (str));
|
||||||
|
|
||||||
|
t.keylen = strlen((char*)t.key);
|
||||||
|
|
||||||
|
t.data = (datatuple::data_t) malloc(10);
|
||||||
|
const char * str2 = "1234567";
|
||||||
|
strcpy((char*)t.data, (str2));
|
||||||
|
|
||||||
|
t.datalen = strlen((char*)t.data);
|
||||||
|
|
||||||
|
std::cout << "size of datatuple:\t" << sizeof(datatuple) << std::endl;
|
||||||
|
std::cout << "keylen:\t" << t.keylen <<
|
||||||
|
"\tdatalen:\t" << t.datalen <<
|
||||||
|
"\t" << t.key <<
|
||||||
|
"\t" << t.data <<
|
||||||
|
std::endl;
|
||||||
|
|
||||||
|
}
|
200
logiterators.cpp
Normal file
200
logiterators.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
//#include "datapage.cpp"
|
||||||
|
#include "logiterators.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//template <class MEMTREE, class TUPLE>
|
||||||
|
/*
|
||||||
|
template <>
|
||||||
|
const byte* toByteArray<std::set<datatuple,datatuple>, datatuple>(
|
||||||
|
memTreeIterator<std::set<datatuple,datatuple>, datatuple> * const t)
|
||||||
|
{
|
||||||
|
return (*(t->it_)).to_bytes();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// tree iterator implementation
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(recordid tree) :
|
||||||
|
tree_(tree),
|
||||||
|
lsmIterator_(logtreeIterator::open(-1,tree)),
|
||||||
|
curr_tuple(0)
|
||||||
|
{
|
||||||
|
init_helper();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(recordid tree, TUPLE& key) :
|
||||||
|
tree_(tree),
|
||||||
|
//scratch_(),
|
||||||
|
lsmIterator_(logtreeIterator::openAt(-1,tree,key.get_key()))//toByteArray())),
|
||||||
|
//slot_(0)
|
||||||
|
{
|
||||||
|
init_helper();
|
||||||
|
|
||||||
|
/*
|
||||||
|
treeIterator * end = this->end();
|
||||||
|
for(;*this != *end && **this < key; ++(*this))
|
||||||
|
{
|
||||||
|
DEBUG("treeIterator was not at the given TUPLE");
|
||||||
|
}
|
||||||
|
delete end;
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::~treeIterator()
|
||||||
|
{
|
||||||
|
if(lsmIterator_)
|
||||||
|
logtreeIterator::close(-1, lsmIterator_);
|
||||||
|
|
||||||
|
if(curr_tuple != NULL)
|
||||||
|
free(curr_tuple);
|
||||||
|
|
||||||
|
if(curr_page!=NULL)
|
||||||
|
{
|
||||||
|
delete curr_page;
|
||||||
|
curr_page = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
void treeIterator<TUPLE>::init_helper()
|
||||||
|
{
|
||||||
|
if(!lsmIterator_)
|
||||||
|
{
|
||||||
|
printf("treeIterator:\t__error__ init_helper():\tnull lsmIterator_");
|
||||||
|
curr_page = 0;
|
||||||
|
dp_itr = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(logtreeIterator::next(-1, lsmIterator_) == 0)
|
||||||
|
{
|
||||||
|
//printf("treeIterator:\t__error__ init_helper():\tlogtreeIteratr::next returned 0." );
|
||||||
|
curr_page = 0;
|
||||||
|
dp_itr = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pageid_t * pid_tmp;
|
||||||
|
pageid_t ** hack = &pid_tmp;
|
||||||
|
logtreeIterator::value(-1,lsmIterator_,(byte**)hack);
|
||||||
|
|
||||||
|
curr_pageid = *pid_tmp;
|
||||||
|
curr_page = new DataPage<TUPLE>(-1, curr_pageid);
|
||||||
|
dp_itr = new DPITR_T(curr_page->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
TUPLE * treeIterator<TUPLE>::getnext()
|
||||||
|
{
|
||||||
|
assert(this->lsmIterator_);
|
||||||
|
|
||||||
|
if(dp_itr == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
TUPLE* readTuple = dp_itr->getnext(-1);
|
||||||
|
|
||||||
|
|
||||||
|
if(!readTuple)
|
||||||
|
{
|
||||||
|
delete dp_itr;
|
||||||
|
dp_itr = 0;
|
||||||
|
delete curr_page;
|
||||||
|
curr_page = 0;
|
||||||
|
|
||||||
|
if(logtreeIterator::next(-1,lsmIterator_))
|
||||||
|
{
|
||||||
|
pageid_t *pid_tmp;
|
||||||
|
|
||||||
|
pageid_t **hack = &pid_tmp;
|
||||||
|
logtreeIterator::value(-1,lsmIterator_,(byte**)hack);
|
||||||
|
curr_pageid = *pid_tmp;
|
||||||
|
curr_page = new DataPage<TUPLE>(-1, curr_pageid);
|
||||||
|
dp_itr = new DPITR_T(curr_page->begin());
|
||||||
|
|
||||||
|
|
||||||
|
readTuple = dp_itr->getnext(-1);
|
||||||
|
assert(readTuple);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: what is this?
|
||||||
|
//past end of iterator! "end" should contain the pageid of the
|
||||||
|
// last leaf, and 1+ numslots on that page.
|
||||||
|
//abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return curr_tuple=readTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(treeIteratorHandle* tree, TUPLE& key) :
|
||||||
|
tree_(tree?tree->r_:NULLRID),
|
||||||
|
scratch_(),
|
||||||
|
lsmIterator_(logtreeIterator::openAt(-1,tree?tree->r_:NULLRID,key.get_key())),//toByteArray())),
|
||||||
|
slot_(0)
|
||||||
|
{
|
||||||
|
init_helper();
|
||||||
|
if(lsmIterator_) {
|
||||||
|
treeIterator * end = this->end();
|
||||||
|
for(;*this != *end && **this < key; ++(*this)) { }
|
||||||
|
delete end;
|
||||||
|
} else {
|
||||||
|
this->slot_ = 0;
|
||||||
|
this->pageid_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(recordid tree, TUPLE &scratch) :
|
||||||
|
tree_(tree),
|
||||||
|
scratch_(scratch),
|
||||||
|
lsmIterator_(logtreeIterator::open(-1,tree)),
|
||||||
|
slot_(0)
|
||||||
|
{
|
||||||
|
init_helper();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(treeIteratorHandle* tree) :
|
||||||
|
tree_(tree?tree->r_:NULLRID),
|
||||||
|
scratch_(),
|
||||||
|
lsmIterator_(logtreeIterator::open(-1,tree?tree->r_:NULLRID)),
|
||||||
|
slot_(0)
|
||||||
|
{
|
||||||
|
init_helper();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
treeIterator<TUPLE>::treeIterator(treeIterator& t) :
|
||||||
|
tree_(t.tree_),
|
||||||
|
scratch_(t.scratch_),
|
||||||
|
lsmIterator_(t.lsmIterator_?logtreeIterator::copy(-1,t.lsmIterator_):0),
|
||||||
|
slot_(t.slot_),
|
||||||
|
pageid_(t.pageid_),
|
||||||
|
p_((Page*)((t.p_)?loadPage(-1,t.p_->id):0))
|
||||||
|
//currentPage_((PAGELAYOUT*)((p_)?p_->impl:0))
|
||||||
|
{
|
||||||
|
if(p_)
|
||||||
|
readlock(p_->rwlatch,0);
|
||||||
|
}
|
||||||
|
*/
|
173
logiterators.h
Normal file
173
logiterators.h
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#ifndef _LOG_ITERATORS_H_
|
||||||
|
#define _LOG_ITERATORS_H_
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stasis/iterator.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
template <class MEMTREE, class TUPLE> class memTreeIterator;
|
||||||
|
|
||||||
|
template <class MEMTREE, class TUPLE>
|
||||||
|
const byte* toByteArray(memTreeIterator<MEMTREE,TUPLE> * const t);
|
||||||
|
|
||||||
|
template <class TUPLE>
|
||||||
|
class DataPage;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// memTreeIterator
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template<class MEMTREE, class TUPLE>
|
||||||
|
class memTreeIterator{
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef typename MEMTREE::const_iterator MTITER;
|
||||||
|
|
||||||
|
public:
|
||||||
|
memTreeIterator( MEMTREE *s )
|
||||||
|
{
|
||||||
|
it_ = s->begin();
|
||||||
|
itend_ = s->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
memTreeIterator( MTITER& it, MTITER& itend )
|
||||||
|
{
|
||||||
|
it_ = it;
|
||||||
|
itend_ = itend;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit memTreeIterator(memTreeIterator &i)
|
||||||
|
{
|
||||||
|
it_ = i.it_;
|
||||||
|
itend_ = i.itend_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TUPLE& operator* ()
|
||||||
|
{
|
||||||
|
return *it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seekEnd()
|
||||||
|
{
|
||||||
|
it_ = itend_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
memTreeIterator * end()
|
||||||
|
{
|
||||||
|
return new memTreeIterator<MEMTREE,TUPLE>(itend_,itend_);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const memTreeIterator &o) const {
|
||||||
|
return it_ == o.it_;
|
||||||
|
}
|
||||||
|
inline bool operator!=(const memTreeIterator &o) const {
|
||||||
|
return !(*this == o);
|
||||||
|
}
|
||||||
|
inline void operator++() {
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
inline void operator--() {
|
||||||
|
--it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int operator-(memTreeIterator &i) {
|
||||||
|
return it_ - i.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator=(memTreeIterator const &i)
|
||||||
|
{
|
||||||
|
it_ = i.it_;
|
||||||
|
itend_ = i.itend_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef MEMTREE* handle;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
MTITER it_;
|
||||||
|
MTITER itend_;
|
||||||
|
|
||||||
|
friend const byte* toByteArray<MEMTREE,TUPLE>(memTreeIterator<MEMTREE,TUPLE> * const t);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class MEMTREE, class TUPLE>
|
||||||
|
const byte* toByteArray(memTreeIterator<MEMTREE,TUPLE> * const t)
|
||||||
|
{
|
||||||
|
return (*(t->it_)).to_bytes();//toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
Scans through an LSM tree's leaf pages, each tuple in the tree, in
|
||||||
|
order. This iterator is designed for maximum forward scan
|
||||||
|
performance, and does not support all STL operations.
|
||||||
|
**/
|
||||||
|
template <class TUPLE>
|
||||||
|
class treeIterator
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
// typedef recordid handle;
|
||||||
|
class treeIteratorHandle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
treeIteratorHandle() : r_(NULLRID) {}
|
||||||
|
treeIteratorHandle(const recordid r) : r_(r) {}
|
||||||
|
|
||||||
|
treeIteratorHandle * operator=(const recordid &r) {
|
||||||
|
r_ = r;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordid r_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef treeIteratorHandle* handle;
|
||||||
|
|
||||||
|
explicit treeIterator(recordid tree);
|
||||||
|
|
||||||
|
explicit treeIterator(recordid tree,TUPLE &key);
|
||||||
|
|
||||||
|
//explicit treeIterator(treeIteratorHandle* tree, TUPLE& key);
|
||||||
|
|
||||||
|
//explicit treeIterator(treeIteratorHandle* tree);
|
||||||
|
|
||||||
|
//explicit treeIterator(treeIterator& t);
|
||||||
|
|
||||||
|
~treeIterator();
|
||||||
|
|
||||||
|
TUPLE * getnext();
|
||||||
|
|
||||||
|
//void advance(int count=1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void init_helper();
|
||||||
|
|
||||||
|
explicit treeIterator() { abort(); }
|
||||||
|
void operator=(treeIterator & t) { abort(); }
|
||||||
|
int operator-(treeIterator & t) { abort(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
recordid tree_; //root of the tree
|
||||||
|
|
||||||
|
lladdIterator_t * lsmIterator_; //logtree iterator
|
||||||
|
|
||||||
|
pageid_t curr_pageid; //current page id
|
||||||
|
DataPage<TUPLE> *curr_page; //current page
|
||||||
|
typedef typename DataPage<TUPLE>::RecordIterator DPITR_T;
|
||||||
|
DPITR_T *dp_itr;
|
||||||
|
TUPLE *curr_tuple; //current tuple
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
649
logserver.cpp
Normal file
649
logserver.cpp
Normal file
|
@ -0,0 +1,649 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "logserver.h"
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
#undef try
|
||||||
|
|
||||||
|
|
||||||
|
//server codes
|
||||||
|
uint8_t logserver::OP_SUCCESS = 1;
|
||||||
|
uint8_t logserver::OP_FAIL = 2;
|
||||||
|
uint8_t logserver::OP_SENDING_TUPLE = 3;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
uint8_t logserver::OP_FIND = 4;
|
||||||
|
uint8_t logserver::OP_INSERT = 5;
|
||||||
|
|
||||||
|
uint8_t logserver::OP_DONE = 6;
|
||||||
|
|
||||||
|
uint8_t logserver::OP_INVALID = 32;
|
||||||
|
|
||||||
|
void *serverLoop(void *args);
|
||||||
|
|
||||||
|
void logserver::startserver(logtable *ltable)
|
||||||
|
{
|
||||||
|
sys_alive = true;
|
||||||
|
this->ltable = ltable;
|
||||||
|
|
||||||
|
selcond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(selcond, 0);
|
||||||
|
|
||||||
|
//initialize threads
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
struct pthread_item *worker_th = new pthread_item;
|
||||||
|
th_list.push_back(worker_th);
|
||||||
|
|
||||||
|
worker_th->th_handle = new pthread_t;
|
||||||
|
struct pthread_data *worker_data = new pthread_data;
|
||||||
|
worker_th->data = worker_data;
|
||||||
|
|
||||||
|
worker_data->idleth_queue = &idleth_queue;
|
||||||
|
worker_data->ready_queue = &ready_queue;
|
||||||
|
worker_data->work_queue = &work_queue;
|
||||||
|
|
||||||
|
worker_data->qlock = qlock;
|
||||||
|
|
||||||
|
worker_data->selcond = selcond;
|
||||||
|
|
||||||
|
worker_data->th_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(worker_data->th_cond,0);
|
||||||
|
|
||||||
|
worker_data->th_mut = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(worker_data->th_mut,0);
|
||||||
|
|
||||||
|
worker_data->workitem = new int;
|
||||||
|
*(worker_data->workitem) = -1;
|
||||||
|
|
||||||
|
//worker_data->table_lock = lsmlock;
|
||||||
|
|
||||||
|
worker_data->ltable = ltable;
|
||||||
|
|
||||||
|
worker_data->sys_alive = &sys_alive;
|
||||||
|
|
||||||
|
pthread_create(worker_th->th_handle, 0, thread_work_fn, worker_th);
|
||||||
|
|
||||||
|
idleth_queue.push(*worker_th);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//start server socket
|
||||||
|
sdata = new serverth_data;
|
||||||
|
sdata->server_socket = &serversocket;
|
||||||
|
sdata->server_port = server_port;
|
||||||
|
sdata->idleth_queue = &idleth_queue;
|
||||||
|
sdata->ready_queue = &ready_queue;
|
||||||
|
sdata->selcond = selcond;
|
||||||
|
sdata->qlock = qlock;
|
||||||
|
|
||||||
|
pthread_create(&server_thread, 0, serverLoop, sdata);
|
||||||
|
|
||||||
|
//start monitoring loop
|
||||||
|
eventLoop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::stopserver()
|
||||||
|
{
|
||||||
|
//close the server socket
|
||||||
|
//stops receiving data on the server socket
|
||||||
|
shutdown(serversocket, 0);
|
||||||
|
|
||||||
|
//wait for all threads to be idle
|
||||||
|
while(idleth_queue.size() != nthreads)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
printf("\n\nSTATISTICS\n");
|
||||||
|
std::map<std::string, int> num_reqsc;
|
||||||
|
std::map<std::string, double> work_timec;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//set the system running flag to false
|
||||||
|
sys_alive = false;
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
pthread_item *idle_th = th_list[i];
|
||||||
|
|
||||||
|
//wake up the thread
|
||||||
|
pthread_mutex_lock(idle_th->data->th_mut);
|
||||||
|
pthread_cond_signal(idle_th->data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th->data->th_mut);
|
||||||
|
//wait for it to join
|
||||||
|
pthread_join(*(idle_th->th_handle), 0);
|
||||||
|
//free the thread variables
|
||||||
|
pthread_cond_destroy(idle_th->data->th_cond);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
if(i == 0)
|
||||||
|
{
|
||||||
|
tot_threadwork_time = 0;
|
||||||
|
num_reqs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tot_threadwork_time += idle_th->data->work_time;
|
||||||
|
num_reqs += idle_th->data->num_reqs;
|
||||||
|
|
||||||
|
printf("thread %d: work_time %.3f\t#calls %d\tavg req process time:\t%.3f\n",
|
||||||
|
i,
|
||||||
|
idle_th->data->work_time,
|
||||||
|
idle_th->data->num_reqs,
|
||||||
|
(( idle_th->data->num_reqs == 0 ) ? 0 : idle_th->data->work_time / idle_th->data->num_reqs)
|
||||||
|
);
|
||||||
|
|
||||||
|
for(std::map<std::string, int>::const_iterator itr = idle_th->data->num_reqsc.begin();
|
||||||
|
itr != idle_th->data->num_reqsc.end(); itr++)
|
||||||
|
{
|
||||||
|
std::string ckey = (*itr).first;
|
||||||
|
printf("\t%s\t%d\t%.3f\t%.3f\n", ckey.c_str(), (*itr).second, idle_th->data->work_timec[ckey],
|
||||||
|
idle_th->data->work_timec[ckey] / (*itr).second);
|
||||||
|
|
||||||
|
if(num_reqsc.find(ckey) == num_reqsc.end()){
|
||||||
|
num_reqsc[ckey] = 0;
|
||||||
|
work_timec[ckey] = 0;
|
||||||
|
}
|
||||||
|
num_reqsc[ckey] += (*itr).second;
|
||||||
|
work_timec[ckey] += idle_th->data->work_timec[ckey];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete idle_th->data->th_cond;
|
||||||
|
delete idle_th->data->th_mut;
|
||||||
|
delete idle_th->data->workitem;
|
||||||
|
delete idle_th->data;
|
||||||
|
delete idle_th->th_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
th_list.clear();
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
|
||||||
|
printf("\n\nAggregated Stats:\n");
|
||||||
|
for(std::map<std::string, int>::const_iterator itr = num_reqsc.begin();
|
||||||
|
itr != num_reqsc.end(); itr++)
|
||||||
|
{
|
||||||
|
std::string ckey = (*itr).first;
|
||||||
|
printf("\t%s\t%d\t%.3f\t%.3f\n", ckey.c_str(), (*itr).second, work_timec[ckey],
|
||||||
|
work_timec[ckey] / (*itr).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
tot_time = (stop_tv.tv_sec - start_tv.tv_sec) * 1000 +
|
||||||
|
(stop_tv.tv_usec / 1000 - start_tv.tv_usec / 1000);
|
||||||
|
|
||||||
|
printf("\ntot time:\t%f\n",tot_time);
|
||||||
|
printf("tot work time:\t%f\n", tot_threadwork_time);
|
||||||
|
printf("load avg:\t%f\n", tot_threadwork_time / tot_time);
|
||||||
|
|
||||||
|
printf("tot num reqs\t%d\n", num_reqs);
|
||||||
|
if(num_reqs!= 0)
|
||||||
|
{
|
||||||
|
printf("tot work time / num reqs:\t%.3f\n", tot_threadwork_time / num_reqs);
|
||||||
|
printf("tot time / num reqs:\t%.3f\n", tot_time / num_reqs );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//close(serversocket);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::eventLoop()
|
||||||
|
{
|
||||||
|
|
||||||
|
fd_set readfs;
|
||||||
|
std::vector<int> sel_list;
|
||||||
|
|
||||||
|
int maxfd;
|
||||||
|
|
||||||
|
struct timeval Timeout;
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
//clear readset
|
||||||
|
FD_ZERO(&readfs);
|
||||||
|
maxfd = -1;
|
||||||
|
|
||||||
|
ts.tv_nsec = 250000; //nanosec
|
||||||
|
ts.tv_sec = 0;
|
||||||
|
|
||||||
|
//Timeout.tv_usec = 250; /* microseconds */
|
||||||
|
//Timeout.tv_sec = 0; /* seconds */
|
||||||
|
|
||||||
|
//update select set
|
||||||
|
pthread_mutex_lock(qlock);
|
||||||
|
|
||||||
|
//while(ready_queue.size() == 0)
|
||||||
|
if(sel_list.size() == 0)
|
||||||
|
{
|
||||||
|
while(ready_queue.size() == 0)
|
||||||
|
pthread_cond_wait(selcond, qlock);
|
||||||
|
//pthread_cond_timedwait(selcond, qlock, &ts);
|
||||||
|
//printf("awoke\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//new connections + processed conns are in ready_queue
|
||||||
|
//add them to select list
|
||||||
|
while(ready_queue.size() > 0)
|
||||||
|
{
|
||||||
|
sel_list.push_back(ready_queue.front());
|
||||||
|
ready_queue.pop();
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
//ready select set
|
||||||
|
for(std::vector<int>::const_iterator itr=sel_list.begin();
|
||||||
|
itr != sel_list.end(); itr++)
|
||||||
|
{
|
||||||
|
if(maxfd < *itr)
|
||||||
|
maxfd = *itr;
|
||||||
|
FD_SET(*itr, &readfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//select events
|
||||||
|
int sel_res = select(maxfd+1, &readfs, NULL, NULL, NULL);// &Timeout);
|
||||||
|
//printf("sel_res %d %d\n", sel_res, errno);
|
||||||
|
//fflush(stdout);
|
||||||
|
//job assignment to threads
|
||||||
|
//printf("sel_list size:\t%d ready_cnt\t%d\n", sel_list.size(), sel_res);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
if(num_selcalls == 0)
|
||||||
|
gettimeofday(&start_tv, 0);
|
||||||
|
|
||||||
|
num_selevents += sel_res;
|
||||||
|
num_selcalls++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_lock(qlock);
|
||||||
|
for(int i=0; i<sel_list.size(); i++ )
|
||||||
|
{
|
||||||
|
int currsock = sel_list[i];
|
||||||
|
|
||||||
|
if (FD_ISSET(currsock, &readfs))
|
||||||
|
{
|
||||||
|
//printf("sock %d ready\n", currsock);
|
||||||
|
// pthread_mutex_lock(qlock);
|
||||||
|
|
||||||
|
if(idleth_queue.size() > 0) //assign the job to an indle thread
|
||||||
|
{
|
||||||
|
pthread_item idle_th = idleth_queue.front();
|
||||||
|
idleth_queue.pop();
|
||||||
|
|
||||||
|
//wake up the thread to do work
|
||||||
|
pthread_mutex_lock(idle_th.data->th_mut);
|
||||||
|
//set the job of the idle thread
|
||||||
|
*(idle_th.data->workitem) = currsock;
|
||||||
|
pthread_cond_signal(idle_th.data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th.data->th_mut);
|
||||||
|
//printf("%d:\tconn %d assigned.\n", i, currsock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//insert the given element to the work queue
|
||||||
|
work_queue.push(currsock);
|
||||||
|
//printf("work queue size:\t%d\n", work_queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
//remove from the sel_list
|
||||||
|
sel_list.erase(sel_list.begin()+i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
gettimeofday(&stop_tv, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *serverLoop(void *args)
|
||||||
|
{
|
||||||
|
|
||||||
|
serverth_data *sdata = (serverth_data*)args;
|
||||||
|
|
||||||
|
int sockfd; //socket descriptor
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct sockaddr_in cli_addr;
|
||||||
|
int newsockfd; //newly created
|
||||||
|
socklen_t clilen = sizeof(cli_addr);
|
||||||
|
|
||||||
|
|
||||||
|
//open a socket
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR opening socket\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
serv_addr.sin_port = htons(sdata->server_port);
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on binding.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//start listening on the server socket
|
||||||
|
//second arg is the max number of coonections waiting in queue
|
||||||
|
if(listen(sockfd,SOMAXCONN)==-1)
|
||||||
|
{
|
||||||
|
printf("ERROR on listen.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LSM Server listenning...\n");
|
||||||
|
|
||||||
|
*(sdata->server_socket) = sockfd;
|
||||||
|
int flag, result;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||||
|
if (newsockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on accept.\n");
|
||||||
|
return 0; // we probably want to continue instead of return here (when not debugging)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = 1;
|
||||||
|
result = setsockopt(newsockfd, /* socket affected */
|
||||||
|
IPPROTO_TCP, /* set option at TCP level */
|
||||||
|
TCP_NODELAY, /* name of option */
|
||||||
|
(char *) &flag, /* the cast is historical
|
||||||
|
cruft */
|
||||||
|
sizeof(int)); /* length of option value */
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on setting socket option TCP_NODELAY.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char clientip[20];
|
||||||
|
inet_ntop(AF_INET, (void*) &(cli_addr.sin_addr), clientip, 20);
|
||||||
|
printf("Connection from:\t%s\n", clientip);
|
||||||
|
|
||||||
|
//printf("Number of idle threads %d\n", idleth_queue.size());
|
||||||
|
|
||||||
|
pthread_mutex_lock(sdata->qlock);
|
||||||
|
|
||||||
|
//insert the given element to the ready queue
|
||||||
|
sdata->ready_queue->push(newsockfd);
|
||||||
|
|
||||||
|
if(sdata->ready_queue->size() == 1) //signal the event loop
|
||||||
|
pthread_cond_signal(sdata->selcond);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(sdata->qlock);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void readfromsocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += read( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void writetosocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += write( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void * thread_work_fn( void * args)
|
||||||
|
{
|
||||||
|
pthread_item * item = (pthread_item *) args;
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->th_mut);
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
while(*(item->data->workitem) == -1)
|
||||||
|
{
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
break;
|
||||||
|
pthread_cond_wait(item->data->th_cond, item->data->th_mut); //wait for job
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
gettimeofday(& (item->data->start_tv), 0);
|
||||||
|
std::ostringstream ostr;
|
||||||
|
ostr << *(item->data->workitem) << "_";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
{
|
||||||
|
//printf("thread quitted.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//step 1: read the opcode
|
||||||
|
uint8_t opcode;
|
||||||
|
ssize_t n = read(*(item->data->workitem), &opcode, sizeof(uint8_t));
|
||||||
|
assert( n == sizeof(uint8_t));
|
||||||
|
assert( opcode < logserver::OP_INVALID );
|
||||||
|
|
||||||
|
if( opcode == logserver::OP_DONE ) //close the conn on failure
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(item->data->qlock);
|
||||||
|
printf("client done. conn closed. (%d, %d, %d, %d)\n",
|
||||||
|
n, errno, *(item->data->workitem), item->data->work_queue->size());
|
||||||
|
close(*(item->data->workitem));
|
||||||
|
|
||||||
|
if(item->data->work_queue->size() > 0)
|
||||||
|
{
|
||||||
|
int new_work = item->data->work_queue->front();
|
||||||
|
item->data->work_queue->pop();
|
||||||
|
//printf("work queue size:\t%d\n", item->data->work_queue->size());
|
||||||
|
*(item->data->workitem) = new_work;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(item->data->qlock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//step 2: read the tuple from client
|
||||||
|
datatuple tuple;
|
||||||
|
tuple.keylen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
tuple.datalen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key length
|
||||||
|
n = read(*(item->data->workitem), tuple.keylen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
//read the data length
|
||||||
|
n = read(*(item->data->workitem), tuple.datalen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key
|
||||||
|
tuple.key = (byte*) malloc(*tuple.keylen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.key, *tuple.keylen);
|
||||||
|
//read the data
|
||||||
|
if(!tuple.isDelete() && opcode != logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
tuple.data = (byte*) malloc(*tuple.datalen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.data, *tuple.datalen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tuple.data = 0;
|
||||||
|
|
||||||
|
//step 3: process the tuple
|
||||||
|
//pthread_mutex_lock(item->data->table_lock);
|
||||||
|
//readlock(item->data->table_lock,0);
|
||||||
|
|
||||||
|
if(opcode == logserver::OP_INSERT)
|
||||||
|
{
|
||||||
|
//insert/update/delete
|
||||||
|
item->data->ltable->insertTuple(tuple);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
//step 4: send response
|
||||||
|
uint8_t rcode = logserver::OP_SUCCESS;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(opcode == logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
//find the tuple
|
||||||
|
datatuple *dt = item->data->ltable->findTuple(-1, tuple.key, *tuple.keylen);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
|
||||||
|
if(dt == 0)
|
||||||
|
printf("key not found:\t%s\n", datatuple::key_to_str(tuple.key).c_str());
|
||||||
|
else if( *dt->datalen != 1024)
|
||||||
|
printf("data len for\t%s:\t%d\n", datatuple::key_to_str(tuple.key).c_str(),
|
||||||
|
*dt->datalen);
|
||||||
|
|
||||||
|
if(datatuple::compare(tuple.key, dt->key) != 0)
|
||||||
|
printf("key not equal:\t%s\t%s\n", datatuple::key_to_str(tuple.key).c_str(),
|
||||||
|
datatuple::key_to_str(dt->key).c_str());
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(dt == 0) //tuple deleted
|
||||||
|
{
|
||||||
|
dt = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
dt->keylen = (uint32_t*) malloc(2*sizeof(uint32_t) + *tuple.keylen);
|
||||||
|
*dt->keylen = *tuple.keylen;
|
||||||
|
dt->datalen = dt->keylen + 1;
|
||||||
|
dt->key = (datatuple::key_t) (dt->datalen+1);
|
||||||
|
memcpy((byte*) dt->key, (byte*) tuple.key, *tuple.keylen);
|
||||||
|
dt->setDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the reply code
|
||||||
|
uint8_t rcode = logserver::OP_SENDING_TUPLE;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
//send the tuple
|
||||||
|
writetosocket(*(item->data->workitem), (byte*) dt->keylen, dt->byte_length());
|
||||||
|
|
||||||
|
//free datatuple
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close the socket
|
||||||
|
//close(*(item->data->workitem));
|
||||||
|
|
||||||
|
//free the tuple
|
||||||
|
free(tuple.keylen);
|
||||||
|
free(tuple.datalen);
|
||||||
|
free(tuple.key);
|
||||||
|
free(tuple.data);
|
||||||
|
|
||||||
|
//printf("socket %d: work completed.", *(item->data->workitem));
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->qlock);
|
||||||
|
|
||||||
|
//add conn desc to ready queue
|
||||||
|
item->data->ready_queue->push(*(item->data->workitem));
|
||||||
|
//printf("ready queue size: %d sock(%d)\n", item->data->ready_queue->size(), *(item->data->workitem));
|
||||||
|
if(item->data->ready_queue->size() == 1) //signal the event loop
|
||||||
|
pthread_cond_signal(item->data->selcond);
|
||||||
|
|
||||||
|
//printf("work complete, added to ready queue %d (size %d)\n", *(item->data->workitem),
|
||||||
|
// item->data->ready_queue->size());
|
||||||
|
|
||||||
|
if(item->data->work_queue->size() > 0)
|
||||||
|
{
|
||||||
|
int new_work = item->data->work_queue->front();
|
||||||
|
item->data->work_queue->pop();
|
||||||
|
//printf("work queue size:\t%d\n", item->data->work_queue->size());
|
||||||
|
*(item->data->workitem) = new_work;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(item->data->qlock);
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
if( item->data->num_reqs == 0 )
|
||||||
|
item->data->work_time = 0;
|
||||||
|
gettimeofday(& (item->data->stop_tv), 0);
|
||||||
|
(item->data->num_reqs)++;
|
||||||
|
//item->data->work_time += tv_to_double(item->data->stop_tv) - tv_to_double(item->data->start_tv);
|
||||||
|
item->data->work_time += (item->data->stop_tv.tv_sec - item->data->start_tv.tv_sec) * 1000 +
|
||||||
|
(item->data->stop_tv.tv_usec / 1000 - item->data->start_tv.tv_usec / 1000);
|
||||||
|
|
||||||
|
int iopcode = opcode;
|
||||||
|
ostr << iopcode;
|
||||||
|
std::string clientkey = ostr.str();
|
||||||
|
if(item->data->num_reqsc.find(clientkey) == item->data->num_reqsc.end())
|
||||||
|
{
|
||||||
|
item->data->num_reqsc[clientkey]=0;
|
||||||
|
item->data->work_timec[clientkey]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
item->data->num_reqsc[clientkey]++;
|
||||||
|
item->data->work_timec[clientkey] += (item->data->stop_tv.tv_sec - item->data->start_tv.tv_sec) * 1000 +
|
||||||
|
(item->data->stop_tv.tv_usec / 1000 - item->data->start_tv.tv_usec / 1000);;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(item->data->th_mut);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
197
logserver.h
Normal file
197
logserver.h
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
#ifndef _LOGSERVER_H_
|
||||||
|
#define _LOGSERVER_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//#include "logstore.h"
|
||||||
|
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stasis/transactional.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef try
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
#define STATS_ENABLED 1
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <map>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class logtable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct pthread_item;
|
||||||
|
|
||||||
|
struct pthread_data {
|
||||||
|
std::queue<pthread_item> *idleth_queue;
|
||||||
|
std::queue<int> *ready_queue;
|
||||||
|
std::queue<int> *work_queue;
|
||||||
|
pthread_mutex_t * qlock;
|
||||||
|
|
||||||
|
pthread_cond_t *selcond;
|
||||||
|
|
||||||
|
pthread_cond_t * th_cond;
|
||||||
|
pthread_mutex_t * th_mut;
|
||||||
|
|
||||||
|
int *workitem; //id of the socket to work
|
||||||
|
|
||||||
|
//pthread_mutex_t * table_lock;
|
||||||
|
//rwl *table_lock;
|
||||||
|
logtable *ltable;
|
||||||
|
bool *sys_alive;
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
int num_reqs;
|
||||||
|
struct timeval start_tv, stop_tv;
|
||||||
|
double work_time;
|
||||||
|
std::map<std::string, int> num_reqsc;
|
||||||
|
std::map<std::string, double> work_timec;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pthread_item{
|
||||||
|
pthread_t * th_handle;
|
||||||
|
pthread_data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//struct work_item
|
||||||
|
//{
|
||||||
|
// int sockd; //socket id
|
||||||
|
// datatuple in_tuple; //request
|
||||||
|
// datatuple out_tuple; //response
|
||||||
|
//};
|
||||||
|
|
||||||
|
struct serverth_data
|
||||||
|
{
|
||||||
|
int *server_socket;
|
||||||
|
int server_port;
|
||||||
|
std::queue<pthread_item> *idleth_queue;
|
||||||
|
std::queue<int> *ready_queue;
|
||||||
|
|
||||||
|
pthread_cond_t *selcond;
|
||||||
|
|
||||||
|
pthread_mutex_t *qlock;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void * thread_work_fn( void *);
|
||||||
|
|
||||||
|
class logserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//server codes
|
||||||
|
static uint8_t OP_SUCCESS;
|
||||||
|
static uint8_t OP_FAIL;
|
||||||
|
static uint8_t OP_SENDING_TUPLE;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
static uint8_t OP_FIND;
|
||||||
|
static uint8_t OP_INSERT;
|
||||||
|
|
||||||
|
static uint8_t OP_DONE;
|
||||||
|
|
||||||
|
static uint8_t OP_INVALID;
|
||||||
|
|
||||||
|
public:
|
||||||
|
logserver(int nthreads, int server_port){
|
||||||
|
this->nthreads = nthreads;
|
||||||
|
this->server_port = server_port;
|
||||||
|
//lsmlock = new pthread_mutex_t;
|
||||||
|
//pthread_mutex_init(lsmlock,0);
|
||||||
|
|
||||||
|
//lsmlock = initlock();
|
||||||
|
|
||||||
|
qlock = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(qlock,0);
|
||||||
|
|
||||||
|
ltable = 0;
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
num_selevents = 0;
|
||||||
|
num_selcalls = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~logserver()
|
||||||
|
{
|
||||||
|
//delete lsmlock;
|
||||||
|
//deletelock(lsmlock);
|
||||||
|
delete qlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startserver(logtable *ltable);
|
||||||
|
|
||||||
|
void stopserver();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//main loop of server
|
||||||
|
//accept connections, assign jobs to threads
|
||||||
|
//void dispatchLoop();
|
||||||
|
|
||||||
|
void eventLoop();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int server_port;
|
||||||
|
|
||||||
|
int nthreads;
|
||||||
|
|
||||||
|
bool sys_alive;
|
||||||
|
|
||||||
|
int serversocket; //server socket file descriptor
|
||||||
|
|
||||||
|
//ccqueue<int> conn_queue; //list of active connections (socket list)
|
||||||
|
|
||||||
|
//ccqueue<pthread_item> idleth_queue; //list of idle threads
|
||||||
|
|
||||||
|
std::queue<int> ready_queue; //connections to go inside select
|
||||||
|
std::queue<int> work_queue; //connections to be processed by worker threads
|
||||||
|
std::queue<pthread_item> idleth_queue;
|
||||||
|
pthread_mutex_t *qlock;
|
||||||
|
|
||||||
|
pthread_t server_thread;
|
||||||
|
serverth_data *sdata;
|
||||||
|
pthread_cond_t *selcond; //server loop cond
|
||||||
|
|
||||||
|
std::vector<pthread_item *> th_list; // list of threads
|
||||||
|
|
||||||
|
//rwl *lsmlock; //lock for using lsm table
|
||||||
|
|
||||||
|
logtable *ltable;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef STATS_ENABLED
|
||||||
|
int num_reqs;
|
||||||
|
int num_selevents;
|
||||||
|
int num_selcalls;
|
||||||
|
struct timeval start_tv, stop_tv;
|
||||||
|
double tot_threadwork_time;
|
||||||
|
double tot_time;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
519
logserver_pers.cpp
Normal file
519
logserver_pers.cpp
Normal file
|
@ -0,0 +1,519 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "logserver.h"
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
#undef try
|
||||||
|
|
||||||
|
|
||||||
|
//server codes
|
||||||
|
uint8_t logserver::OP_SUCCESS = 1;
|
||||||
|
uint8_t logserver::OP_FAIL = 2;
|
||||||
|
uint8_t logserver::OP_SENDING_TUPLE = 3;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
uint8_t logserver::OP_FIND = 4;
|
||||||
|
uint8_t logserver::OP_INSERT = 5;
|
||||||
|
|
||||||
|
uint8_t logserver::OP_DONE = 6;
|
||||||
|
|
||||||
|
uint8_t logserver::OP_INVALID = 32;
|
||||||
|
|
||||||
|
void *serverLoop(void *args);
|
||||||
|
|
||||||
|
void logserver::startserver(logtable *ltable)
|
||||||
|
{
|
||||||
|
sys_alive = true;
|
||||||
|
this->ltable = ltable;
|
||||||
|
|
||||||
|
selcond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(selcond, 0);
|
||||||
|
|
||||||
|
//initialize threads
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
struct pthread_item *worker_th = new pthread_item;
|
||||||
|
th_list.push_back(worker_th);
|
||||||
|
|
||||||
|
worker_th->th_handle = new pthread_t;
|
||||||
|
struct pthread_data *worker_data = new pthread_data;
|
||||||
|
worker_th->data = worker_data;
|
||||||
|
|
||||||
|
worker_data->idleth_queue = &idleth_queue;
|
||||||
|
worker_data->ready_queue = &ready_queue;
|
||||||
|
worker_data->work_queue = &work_queue;
|
||||||
|
|
||||||
|
worker_data->qlock = qlock;
|
||||||
|
|
||||||
|
worker_data->selcond = selcond;
|
||||||
|
|
||||||
|
worker_data->th_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(worker_data->th_cond,0);
|
||||||
|
|
||||||
|
worker_data->th_mut = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(worker_data->th_mut,0);
|
||||||
|
|
||||||
|
worker_data->workitem = new int;
|
||||||
|
*(worker_data->workitem) = -1;
|
||||||
|
|
||||||
|
worker_data->table_lock = lsmlock;
|
||||||
|
|
||||||
|
worker_data->ltable = ltable;
|
||||||
|
|
||||||
|
worker_data->sys_alive = &sys_alive;
|
||||||
|
|
||||||
|
pthread_create(worker_th->th_handle, 0, thread_work_fn, worker_th);
|
||||||
|
|
||||||
|
idleth_queue.push(*worker_th);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//start server socket
|
||||||
|
sdata = new serverth_data;
|
||||||
|
sdata->server_socket = &serversocket;
|
||||||
|
sdata->server_port = server_port;
|
||||||
|
sdata->idleth_queue = &idleth_queue;
|
||||||
|
sdata->ready_queue = &ready_queue;
|
||||||
|
sdata->selcond = selcond;
|
||||||
|
sdata->qlock = qlock;
|
||||||
|
|
||||||
|
pthread_create(&server_thread, 0, serverLoop, sdata);
|
||||||
|
|
||||||
|
//start monitoring loop
|
||||||
|
eventLoop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::stopserver()
|
||||||
|
{
|
||||||
|
//close the server socket
|
||||||
|
//stops receiving data on the server socket
|
||||||
|
shutdown(serversocket, 0);
|
||||||
|
|
||||||
|
//wait for all threads to be idle
|
||||||
|
while(idleth_queue.size() != nthreads)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
//set the system running flag to false
|
||||||
|
sys_alive = false;
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
pthread_item *idle_th = th_list[i];
|
||||||
|
|
||||||
|
//wake up the thread
|
||||||
|
pthread_mutex_lock(idle_th->data->th_mut);
|
||||||
|
pthread_cond_signal(idle_th->data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th->data->th_mut);
|
||||||
|
//wait for it to join
|
||||||
|
pthread_join(*(idle_th->th_handle), 0);
|
||||||
|
//free the thread variables
|
||||||
|
pthread_cond_destroy(idle_th->data->th_cond);
|
||||||
|
delete idle_th->data->th_cond;
|
||||||
|
delete idle_th->data->th_mut;
|
||||||
|
delete idle_th->data->workitem;
|
||||||
|
delete idle_th->data;
|
||||||
|
delete idle_th->th_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
th_list.clear();
|
||||||
|
|
||||||
|
//close(serversocket);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::eventLoop()
|
||||||
|
{
|
||||||
|
|
||||||
|
fd_set readfs;
|
||||||
|
std::vector<int> sel_list;
|
||||||
|
|
||||||
|
int maxfd;
|
||||||
|
|
||||||
|
struct timeval Timeout;
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
//clear readset
|
||||||
|
FD_ZERO(&readfs);
|
||||||
|
maxfd = -1;
|
||||||
|
|
||||||
|
ts.tv_nsec = 250000; //nanosec
|
||||||
|
ts.tv_sec = 0;
|
||||||
|
|
||||||
|
//Timeout.tv_usec = 250; /* microseconds */
|
||||||
|
//Timeout.tv_sec = 0; /* seconds */
|
||||||
|
|
||||||
|
//update select set
|
||||||
|
pthread_mutex_lock(qlock);
|
||||||
|
|
||||||
|
while(ready_queue.size() == 0)
|
||||||
|
{
|
||||||
|
pthread_cond_wait(selcond, qlock);
|
||||||
|
//pthread_cond_timedwait(selcond, qlock, &ts);
|
||||||
|
//printf("awoke\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//new connections + processed conns are in ready_queue
|
||||||
|
//add them to select list
|
||||||
|
while(ready_queue.size() > 0)
|
||||||
|
{
|
||||||
|
sel_list.push_back(ready_queue.front());
|
||||||
|
ready_queue.pop();
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
//ready select set
|
||||||
|
for(std::vector<int>::const_iterator itr=sel_list.begin();
|
||||||
|
itr != sel_list.end(); itr++)
|
||||||
|
{
|
||||||
|
if(maxfd < *itr)
|
||||||
|
maxfd = *itr;
|
||||||
|
FD_SET(*itr, &readfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//select events
|
||||||
|
int sel_res = select(maxfd+1, &readfs, NULL, NULL, NULL);// &Timeout);
|
||||||
|
//printf("sel_res %d %d\n", sel_res, errno);
|
||||||
|
//fflush(stdout);
|
||||||
|
//job assignment to threads
|
||||||
|
|
||||||
|
for(int i=0; i<sel_list.size(); i++ )
|
||||||
|
{
|
||||||
|
int currsock = sel_list[i];
|
||||||
|
|
||||||
|
if (FD_ISSET(currsock, &readfs))
|
||||||
|
{
|
||||||
|
//printf("sock %d ready\n", currsock);
|
||||||
|
pthread_mutex_lock(qlock);
|
||||||
|
|
||||||
|
if(idleth_queue.size() > 0) //assign the job to an indle thread
|
||||||
|
{
|
||||||
|
pthread_item idle_th = idleth_queue.front();
|
||||||
|
idleth_queue.pop();
|
||||||
|
|
||||||
|
//wake up the thread to do work
|
||||||
|
pthread_mutex_lock(idle_th.data->th_mut);
|
||||||
|
//set the job of the idle thread
|
||||||
|
*(idle_th.data->workitem) = currsock;
|
||||||
|
pthread_cond_signal(idle_th.data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th.data->th_mut);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//insert the given element to the work queue
|
||||||
|
work_queue.push(currsock);
|
||||||
|
printf("work queue size:\t%d\n", work_queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove from the sel_list
|
||||||
|
sel_list.erase(sel_list.begin()+i);
|
||||||
|
i--;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *serverLoop(void *args)
|
||||||
|
{
|
||||||
|
|
||||||
|
serverth_data *sdata = (serverth_data*)args;
|
||||||
|
|
||||||
|
int sockfd; //socket descriptor
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct sockaddr_in cli_addr;
|
||||||
|
int newsockfd; //newly created
|
||||||
|
socklen_t clilen = sizeof(cli_addr);
|
||||||
|
|
||||||
|
|
||||||
|
//open a socket
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR opening socket\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
serv_addr.sin_port = htons(sdata->server_port);
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on binding.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//start listening on the server socket
|
||||||
|
//second arg is the max number of coonections waiting in queue
|
||||||
|
if(listen(sockfd,SOMAXCONN)==-1)
|
||||||
|
{
|
||||||
|
printf("ERROR on listen.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LSM Server listenning...\n");
|
||||||
|
|
||||||
|
*(sdata->server_socket) = sockfd;
|
||||||
|
int flag, result;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||||
|
if (newsockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on accept.\n");
|
||||||
|
return 0; // we probably want to continue instead of return here (when not debugging)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = 1;
|
||||||
|
result = setsockopt(newsockfd, /* socket affected */
|
||||||
|
IPPROTO_TCP, /* set option at TCP level */
|
||||||
|
TCP_NODELAY, /* name of option */
|
||||||
|
(char *) &flag, /* the cast is historical
|
||||||
|
cruft */
|
||||||
|
sizeof(int)); /* length of option value */
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on setting socket option TCP_NODELAY.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char clientip[20];
|
||||||
|
inet_ntop(AF_INET, (void*) &(cli_addr.sin_addr), clientip, 20);
|
||||||
|
printf("Connection from:\t%s\n", clientip);
|
||||||
|
|
||||||
|
//printf("Number of idle threads %d\n", idleth_queue.size());
|
||||||
|
|
||||||
|
pthread_mutex_lock(sdata->qlock);
|
||||||
|
|
||||||
|
//insert the given element to the ready queue
|
||||||
|
sdata->ready_queue->push(newsockfd);
|
||||||
|
|
||||||
|
if(sdata->ready_queue->size() == 1) //signal the event loop
|
||||||
|
pthread_cond_signal(sdata->selcond);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(sdata->qlock);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void readfromsocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += read( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void writetosocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += write( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void * thread_work_fn( void * args)
|
||||||
|
{
|
||||||
|
pthread_item * item = (pthread_item *) args;
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->th_mut);
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
while(*(item->data->workitem) == -1)
|
||||||
|
{
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
break;
|
||||||
|
pthread_cond_wait(item->data->th_cond, item->data->th_mut); //wait for job
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
{
|
||||||
|
//printf("thread quitted.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//step 1: read the opcode
|
||||||
|
uint8_t opcode;
|
||||||
|
ssize_t n = read(*(item->data->workitem), &opcode, sizeof(uint8_t));
|
||||||
|
assert( n == sizeof(uint8_t));
|
||||||
|
assert( opcode < logserver::OP_INVALID );
|
||||||
|
|
||||||
|
if( opcode == logserver::OP_DONE ) //close the conn on failure
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(item->data->qlock);
|
||||||
|
printf("client done. conn closed. (%d, %d, %d, %d)\n",
|
||||||
|
n, errno, *(item->data->workitem), item->data->work_queue->size());
|
||||||
|
close(*(item->data->workitem));
|
||||||
|
|
||||||
|
if(item->data->work_queue->size() > 0)
|
||||||
|
{
|
||||||
|
int new_work = item->data->work_queue->front();
|
||||||
|
item->data->work_queue->pop();
|
||||||
|
printf("work queue size:\t%d\n", item->data->work_queue->size());
|
||||||
|
*(item->data->workitem) = new_work;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_cond_signal(item->data->selcond);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(item->data->qlock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//step 2: read the tuple from client
|
||||||
|
datatuple tuple;
|
||||||
|
tuple.keylen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
tuple.datalen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key length
|
||||||
|
n = read(*(item->data->workitem), tuple.keylen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
//read the data length
|
||||||
|
n = read(*(item->data->workitem), tuple.datalen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key
|
||||||
|
tuple.key = (byte*) malloc(*tuple.keylen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.key, *tuple.keylen);
|
||||||
|
//read the data
|
||||||
|
if(!tuple.isDelete() && opcode != logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
tuple.data = (byte*) malloc(*tuple.datalen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.data, *tuple.datalen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tuple.data = 0;
|
||||||
|
|
||||||
|
//step 3: process the tuple
|
||||||
|
//pthread_mutex_lock(item->data->table_lock);
|
||||||
|
//readlock(item->data->table_lock,0);
|
||||||
|
|
||||||
|
if(opcode == logserver::OP_INSERT)
|
||||||
|
{
|
||||||
|
//insert/update/delete
|
||||||
|
item->data->ltable->insertTuple(tuple);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
//step 4: send response
|
||||||
|
uint8_t rcode = logserver::OP_SUCCESS;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(opcode == logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
//find the tuple
|
||||||
|
datatuple *dt = item->data->ltable->findTuple(-1, tuple.key, *tuple.keylen);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
|
||||||
|
if(dt == 0) //tuple deleted
|
||||||
|
{
|
||||||
|
dt = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
dt->keylen = (uint32_t*) malloc(2*sizeof(uint32_t) + *tuple.keylen);
|
||||||
|
*dt->keylen = *tuple.keylen;
|
||||||
|
dt->datalen = dt->keylen + 1;
|
||||||
|
dt->key = (datatuple::key_t) (dt->datalen+1);
|
||||||
|
memcpy((byte*) dt->key, (byte*) tuple.key, *tuple.keylen);
|
||||||
|
dt->setDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the reply code
|
||||||
|
uint8_t rcode = logserver::OP_SENDING_TUPLE;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
//send the tuple
|
||||||
|
writetosocket(*(item->data->workitem), (byte*) dt->keylen, dt->byte_length());
|
||||||
|
|
||||||
|
//free datatuple
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close the socket
|
||||||
|
//close(*(item->data->workitem));
|
||||||
|
|
||||||
|
//free the tuple
|
||||||
|
free(tuple.keylen);
|
||||||
|
free(tuple.datalen);
|
||||||
|
free(tuple.key);
|
||||||
|
free(tuple.data);
|
||||||
|
|
||||||
|
//printf("socket %d: work completed.\n", *(item->data->workitem));
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->qlock);
|
||||||
|
|
||||||
|
//add conn desc to ready queue
|
||||||
|
item->data->ready_queue->push(*(item->data->workitem));
|
||||||
|
//printf("ready queue size: %d sock(%d)\n", item->data->ready_queue->size(), *(item->data->workitem));
|
||||||
|
if(item->data->ready_queue->size() == 1) //signal the event loop
|
||||||
|
pthread_cond_signal(item->data->selcond);
|
||||||
|
|
||||||
|
if(item->data->work_queue->size() > 0)
|
||||||
|
{
|
||||||
|
int new_work = item->data->work_queue->front();
|
||||||
|
item->data->work_queue->pop();
|
||||||
|
printf("work queue size:\t%d\n", item->data->work_queue->size());
|
||||||
|
*(item->data->workitem) = new_work;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(item->data->qlock);
|
||||||
|
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(item->data->th_mut);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
163
logserver_pers.h
Normal file
163
logserver_pers.h
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
#ifndef _LOGSERVER_H_
|
||||||
|
#define _LOGSERVER_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//#include "logstore.h"
|
||||||
|
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stasis/transactional.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef try
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
class logtable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct pthread_item;
|
||||||
|
|
||||||
|
struct pthread_data {
|
||||||
|
std::queue<pthread_item> *idleth_queue;
|
||||||
|
std::queue<int> *ready_queue;
|
||||||
|
std::queue<int> *work_queue;
|
||||||
|
pthread_mutex_t * qlock;
|
||||||
|
|
||||||
|
pthread_cond_t *selcond;
|
||||||
|
|
||||||
|
pthread_cond_t * th_cond;
|
||||||
|
pthread_mutex_t * th_mut;
|
||||||
|
|
||||||
|
int *workitem; //id of the socket to work
|
||||||
|
|
||||||
|
//pthread_mutex_t * table_lock;
|
||||||
|
rwl *table_lock;
|
||||||
|
logtable *ltable;
|
||||||
|
bool *sys_alive;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pthread_item{
|
||||||
|
pthread_t * th_handle;
|
||||||
|
pthread_data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//struct work_item
|
||||||
|
//{
|
||||||
|
// int sockd; //socket id
|
||||||
|
// datatuple in_tuple; //request
|
||||||
|
// datatuple out_tuple; //response
|
||||||
|
//};
|
||||||
|
|
||||||
|
struct serverth_data
|
||||||
|
{
|
||||||
|
int *server_socket;
|
||||||
|
int server_port;
|
||||||
|
std::queue<pthread_item> *idleth_queue;
|
||||||
|
std::queue<int> *ready_queue;
|
||||||
|
|
||||||
|
pthread_cond_t *selcond;
|
||||||
|
|
||||||
|
pthread_mutex_t *qlock;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void * thread_work_fn( void *);
|
||||||
|
|
||||||
|
class logserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//server codes
|
||||||
|
static uint8_t OP_SUCCESS;
|
||||||
|
static uint8_t OP_FAIL;
|
||||||
|
static uint8_t OP_SENDING_TUPLE;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
static uint8_t OP_FIND;
|
||||||
|
static uint8_t OP_INSERT;
|
||||||
|
|
||||||
|
static uint8_t OP_DONE;
|
||||||
|
|
||||||
|
static uint8_t OP_INVALID;
|
||||||
|
|
||||||
|
public:
|
||||||
|
logserver(int nthreads, int server_port){
|
||||||
|
this->nthreads = nthreads;
|
||||||
|
this->server_port = server_port;
|
||||||
|
//lsmlock = new pthread_mutex_t;
|
||||||
|
//pthread_mutex_init(lsmlock,0);
|
||||||
|
|
||||||
|
lsmlock = initlock();
|
||||||
|
|
||||||
|
qlock = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(qlock,0);
|
||||||
|
|
||||||
|
ltable = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~logserver()
|
||||||
|
{
|
||||||
|
//delete lsmlock;
|
||||||
|
deletelock(lsmlock);
|
||||||
|
delete qlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startserver(logtable *ltable);
|
||||||
|
|
||||||
|
void stopserver();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//main loop of server
|
||||||
|
//accept connections, assign jobs to threads
|
||||||
|
//void dispatchLoop();
|
||||||
|
|
||||||
|
void eventLoop();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int server_port;
|
||||||
|
|
||||||
|
int nthreads;
|
||||||
|
|
||||||
|
bool sys_alive;
|
||||||
|
|
||||||
|
int serversocket; //server socket file descriptor
|
||||||
|
|
||||||
|
//ccqueue<int> conn_queue; //list of active connections (socket list)
|
||||||
|
|
||||||
|
//ccqueue<pthread_item> idleth_queue; //list of idle threads
|
||||||
|
|
||||||
|
std::queue<int> ready_queue; //connections to go inside select
|
||||||
|
std::queue<int> work_queue; //connections to be processed by worker threads
|
||||||
|
std::queue<pthread_item> idleth_queue;
|
||||||
|
pthread_mutex_t *qlock;
|
||||||
|
|
||||||
|
pthread_t server_thread;
|
||||||
|
serverth_data *sdata;
|
||||||
|
pthread_cond_t *selcond; //server loop cond
|
||||||
|
|
||||||
|
std::vector<pthread_item *> th_list; // list of threads
|
||||||
|
|
||||||
|
rwl *lsmlock; //lock for using lsm table
|
||||||
|
|
||||||
|
logtable *ltable;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
409
logserver_simple.cpp
Normal file
409
logserver_simple.cpp
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "logserver.h"
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef end
|
||||||
|
#undef try
|
||||||
|
|
||||||
|
|
||||||
|
//server codes
|
||||||
|
uint8_t logserver::OP_SUCCESS = 1;
|
||||||
|
uint8_t logserver::OP_FAIL = 2;
|
||||||
|
uint8_t logserver::OP_SENDING_TUPLE = 3;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
uint8_t logserver::OP_FIND = 4;
|
||||||
|
uint8_t logserver::OP_INSERT = 5;
|
||||||
|
|
||||||
|
uint8_t logserver::OP_INVALID = 32;
|
||||||
|
|
||||||
|
|
||||||
|
void logserver::startserver(logtable *ltable)
|
||||||
|
{
|
||||||
|
sys_alive = true;
|
||||||
|
this->ltable = ltable;
|
||||||
|
//initialize threads
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
struct pthread_item *worker_th = new pthread_item;
|
||||||
|
th_list.push_back(worker_th);
|
||||||
|
|
||||||
|
worker_th->th_handle = new pthread_t;
|
||||||
|
struct pthread_data *worker_data = new pthread_data;
|
||||||
|
worker_th->data = worker_data;
|
||||||
|
|
||||||
|
worker_data->idleth_queue = &idleth_queue;
|
||||||
|
|
||||||
|
worker_data->conn_queue = &conn_queue;
|
||||||
|
|
||||||
|
worker_data->qlock = qlock;
|
||||||
|
|
||||||
|
worker_data->th_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(worker_data->th_cond,0);
|
||||||
|
|
||||||
|
worker_data->th_mut = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(worker_data->th_mut,0);
|
||||||
|
|
||||||
|
worker_data->workitem = new int;
|
||||||
|
*(worker_data->workitem) = -1;
|
||||||
|
|
||||||
|
worker_data->table_lock = lsmlock;
|
||||||
|
|
||||||
|
worker_data->ltable = ltable;
|
||||||
|
|
||||||
|
worker_data->sys_alive = &sys_alive;
|
||||||
|
|
||||||
|
pthread_create(worker_th->th_handle, 0, thread_work_fn, worker_th);
|
||||||
|
|
||||||
|
idleth_queue.push(*worker_th);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchLoop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::stopserver()
|
||||||
|
{
|
||||||
|
//close the server socket
|
||||||
|
//stops receiving data on the server socket
|
||||||
|
shutdown(serversocket, 0);
|
||||||
|
|
||||||
|
//wait for all threads to be idle
|
||||||
|
while(idleth_queue.size() != nthreads)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
//set the system running flag to false
|
||||||
|
sys_alive = false;
|
||||||
|
for(int i=0; i<nthreads; i++)
|
||||||
|
{
|
||||||
|
pthread_item *idle_th = th_list[i];
|
||||||
|
|
||||||
|
//wake up the thread
|
||||||
|
pthread_mutex_lock(idle_th->data->th_mut);
|
||||||
|
pthread_cond_signal(idle_th->data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th->data->th_mut);
|
||||||
|
//wait for it to join
|
||||||
|
pthread_join(*(idle_th->th_handle), 0);
|
||||||
|
//free the thread variables
|
||||||
|
pthread_cond_destroy(idle_th->data->th_cond);
|
||||||
|
delete idle_th->data->th_cond;
|
||||||
|
delete idle_th->data->th_mut;
|
||||||
|
delete idle_th->data->workitem;
|
||||||
|
delete idle_th->data;
|
||||||
|
delete idle_th->th_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
th_list.clear();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logserver::dispatchLoop()
|
||||||
|
{
|
||||||
|
|
||||||
|
int sockfd; //socket descriptor
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct sockaddr_in cli_addr;
|
||||||
|
int newsockfd; //newly created
|
||||||
|
socklen_t clilen = sizeof(cli_addr);
|
||||||
|
|
||||||
|
|
||||||
|
//open a socket
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR opening socket\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
serv_addr.sin_port = htons(server_port);
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on binding.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//start listening on the server socket
|
||||||
|
//second arg is the max number of coonections waiting in queue
|
||||||
|
if(listen(sockfd,SOMAXCONN)==-1)
|
||||||
|
{
|
||||||
|
printf("ERROR on listen.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LSM Server listenning...\n");
|
||||||
|
|
||||||
|
serversocket = sockfd;
|
||||||
|
int flag, result;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
|
||||||
|
if (newsockfd < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on accept.\n");
|
||||||
|
return; // we probably want to continue instead of return here (when not debugging)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = 1;
|
||||||
|
result = setsockopt(newsockfd, /* socket affected */
|
||||||
|
IPPROTO_TCP, /* set option at TCP level */
|
||||||
|
TCP_NODELAY, /* name of option */
|
||||||
|
(char *) &flag, /* the cast is historical
|
||||||
|
cruft */
|
||||||
|
sizeof(int)); /* length of option value */
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
printf("ERROR on setting socket option TCP_NODELAY.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char clientip[20];
|
||||||
|
inet_ntop(AF_INET, (void*) &(cli_addr.sin_addr), clientip, 20);
|
||||||
|
//printf("Connection from:\t%s\n", clientip);
|
||||||
|
|
||||||
|
//printf("Number of idle threads %d\n", idleth_queue.size());
|
||||||
|
|
||||||
|
pthread_mutex_lock(qlock);
|
||||||
|
|
||||||
|
if(idleth_queue.size() > 0)
|
||||||
|
{
|
||||||
|
pthread_item idle_th = idleth_queue.front();
|
||||||
|
idleth_queue.pop();
|
||||||
|
|
||||||
|
//wake up the thread to do work
|
||||||
|
pthread_mutex_lock(idle_th.data->th_mut);
|
||||||
|
//set the job of the idle thread
|
||||||
|
*(idle_th.data->workitem) = newsockfd;
|
||||||
|
pthread_cond_signal(idle_th.data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th.data->th_mut);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//insert the given element to the queue
|
||||||
|
conn_queue.push(newsockfd);
|
||||||
|
//printf("Number of queued connections:\t%d\n", conn_queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(qlock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
pthread_item idle_th = idleth_queue.pop();
|
||||||
|
//wake up the thread to do work
|
||||||
|
pthread_mutex_lock(idle_th.data->th_mut);
|
||||||
|
//set the job of the idle thread
|
||||||
|
*(idle_th.data->workitem) = newsockfd;
|
||||||
|
pthread_cond_signal(idle_th.data->th_cond);
|
||||||
|
pthread_mutex_unlock(idle_th.data->th_mut);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(int empty_exception)
|
||||||
|
{
|
||||||
|
//insert the given element to the queue
|
||||||
|
conn_queue.push(newsockfd);
|
||||||
|
//printf("Number of queued connections:\t%d\n", conn_queue.size());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void readfromsocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += read( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void writetosocket(int sockd, byte *buf, int count)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
while( n < count )
|
||||||
|
{
|
||||||
|
n += write( sockd, buf + n, count - n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void * thread_work_fn( void * args)
|
||||||
|
{
|
||||||
|
pthread_item * item = (pthread_item *) args;
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->th_mut);
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
while(*(item->data->workitem) == -1)
|
||||||
|
{
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
break;
|
||||||
|
pthread_cond_wait(item->data->th_cond, item->data->th_mut); //wait for job
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!*(item->data->sys_alive))
|
||||||
|
{
|
||||||
|
//printf("thread quitted.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//step 1: read the opcode
|
||||||
|
uint8_t opcode;
|
||||||
|
ssize_t n = read(*(item->data->workitem), &opcode, sizeof(uint8_t));
|
||||||
|
assert( n == sizeof(uint8_t));
|
||||||
|
assert( opcode < logserver::OP_INVALID );
|
||||||
|
|
||||||
|
//step 2: read the tuple from client
|
||||||
|
datatuple tuple;
|
||||||
|
tuple.keylen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
tuple.datalen = (uint32_t*)malloc(sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key length
|
||||||
|
n = read(*(item->data->workitem), tuple.keylen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
//read the data length
|
||||||
|
n = read(*(item->data->workitem), tuple.datalen, sizeof(uint32_t));
|
||||||
|
assert( n == sizeof(uint32_t));
|
||||||
|
|
||||||
|
//read the key
|
||||||
|
tuple.key = (byte*) malloc(*tuple.keylen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.key, *tuple.keylen);
|
||||||
|
//read the data
|
||||||
|
if(!tuple.isDelete() && opcode != logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
tuple.data = (byte*) malloc(*tuple.datalen);
|
||||||
|
readfromsocket(*(item->data->workitem), (byte*) tuple.data, *tuple.datalen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tuple.data = 0;
|
||||||
|
|
||||||
|
//step 3: process the tuple
|
||||||
|
//pthread_mutex_lock(item->data->table_lock);
|
||||||
|
//readlock(item->data->table_lock,0);
|
||||||
|
|
||||||
|
if(opcode == logserver::OP_INSERT)
|
||||||
|
{
|
||||||
|
//insert/update/delete
|
||||||
|
item->data->ltable->insertTuple(tuple);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
//step 4: send response
|
||||||
|
uint8_t rcode = logserver::OP_SUCCESS;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(opcode == logserver::OP_FIND)
|
||||||
|
{
|
||||||
|
//find the tuple
|
||||||
|
datatuple *dt = item->data->ltable->findTuple(-1, tuple.key, *tuple.keylen);
|
||||||
|
//unlock the lsmlock
|
||||||
|
//pthread_mutex_unlock(item->data->table_lock);
|
||||||
|
//unlock(item->data->table_lock);
|
||||||
|
|
||||||
|
if(dt == 0) //tuple deleted
|
||||||
|
{
|
||||||
|
dt = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
dt->keylen = (uint32_t*) malloc(2*sizeof(uint32_t) + *tuple.keylen);
|
||||||
|
*dt->keylen = *tuple.keylen;
|
||||||
|
dt->datalen = dt->keylen + 1;
|
||||||
|
dt->key = (datatuple::key_t) (dt->datalen+1);
|
||||||
|
memcpy((byte*) dt->key, (byte*) tuple.key, *tuple.keylen);
|
||||||
|
dt->setDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the reply code
|
||||||
|
uint8_t rcode = logserver::OP_SENDING_TUPLE;
|
||||||
|
n = write(*(item->data->workitem), &rcode, sizeof(uint8_t));
|
||||||
|
assert(n == sizeof(uint8_t));
|
||||||
|
|
||||||
|
//send the tuple
|
||||||
|
writetosocket(*(item->data->workitem), (byte*) dt->keylen, dt->byte_length());
|
||||||
|
|
||||||
|
//free datatuple
|
||||||
|
free(dt->keylen);
|
||||||
|
free(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close the socket
|
||||||
|
close(*(item->data->workitem));
|
||||||
|
|
||||||
|
//free the tuple
|
||||||
|
free(tuple.keylen);
|
||||||
|
free(tuple.datalen);
|
||||||
|
free(tuple.key);
|
||||||
|
free(tuple.data);
|
||||||
|
|
||||||
|
//printf("socket %d: work completed.\n", *(item->data->workitem));
|
||||||
|
|
||||||
|
pthread_mutex_lock(item->data->qlock);
|
||||||
|
|
||||||
|
if(item->data->conn_queue->size() > 0)
|
||||||
|
{
|
||||||
|
int new_work = item->data->conn_queue->front();
|
||||||
|
item->data->conn_queue->pop();
|
||||||
|
*(item->data->workitem) = new_work;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(item->data->qlock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
//check if there is new work this thread can do
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int new_work = item->data->conn_queue->pop();
|
||||||
|
*(item->data->workitem) = new_work; //set new work
|
||||||
|
//printf("socket %d: new work found.\n", *(item->data->workitem));
|
||||||
|
}
|
||||||
|
catch(int empty_exception)
|
||||||
|
{
|
||||||
|
//printf("socket %d: no new work found.\n", *(item->data->workitem));
|
||||||
|
//set work to -1
|
||||||
|
*(item->data->workitem) = -1;
|
||||||
|
//add self to idle queue
|
||||||
|
item->data->idleth_queue->push(*item);
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(item->data->th_mut);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
198
logserver_simple.h
Normal file
198
logserver_simple.h
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
#ifndef _LOGSERVER_H_
|
||||||
|
#define _LOGSERVER_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//#include "logstore.h"
|
||||||
|
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stasis/transactional.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#undef begin
|
||||||
|
#undef try
|
||||||
|
#undef end
|
||||||
|
|
||||||
|
class logtable;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class ccqueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ccqueue()
|
||||||
|
{
|
||||||
|
qmut = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(qmut,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(qmut);
|
||||||
|
int qsize = m_queue.size();
|
||||||
|
pthread_mutex_unlock(qmut);
|
||||||
|
return qsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
//inserts a copy of the given element to the queue
|
||||||
|
void push(const T &item)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(qmut);
|
||||||
|
m_queue.push(item);
|
||||||
|
pthread_mutex_unlock(qmut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns a copy of the next element
|
||||||
|
//deletes the copy in the queue
|
||||||
|
//throws an exception with -1 on empty queue
|
||||||
|
T pop() throw (int)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(qmut);
|
||||||
|
|
||||||
|
if(m_queue.size() > 0)
|
||||||
|
{
|
||||||
|
T item = m_queue.front();
|
||||||
|
m_queue.pop();
|
||||||
|
pthread_mutex_unlock(qmut);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pthread_mutex_unlock(qmut);
|
||||||
|
throw(-1);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
~ccqueue()
|
||||||
|
{
|
||||||
|
delete qmut;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::queue<T> m_queue;
|
||||||
|
|
||||||
|
pthread_mutex_t *qmut;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pthread_item;
|
||||||
|
|
||||||
|
struct pthread_data {
|
||||||
|
std::queue<pthread_item> *idleth_queue;
|
||||||
|
std::queue<int> *conn_queue;
|
||||||
|
pthread_mutex_t * qlock;
|
||||||
|
|
||||||
|
pthread_cond_t * th_cond;
|
||||||
|
pthread_mutex_t * th_mut;
|
||||||
|
|
||||||
|
int *workitem; //id of the socket to work
|
||||||
|
|
||||||
|
//pthread_mutex_t * table_lock;
|
||||||
|
rwl *table_lock;
|
||||||
|
logtable *ltable;
|
||||||
|
bool *sys_alive;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pthread_item{
|
||||||
|
pthread_t * th_handle;
|
||||||
|
pthread_data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct work_item
|
||||||
|
{
|
||||||
|
int sockd; //socket id
|
||||||
|
datatuple in_tuple; //request
|
||||||
|
datatuple out_tuple; //response
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void * thread_work_fn( void *);
|
||||||
|
|
||||||
|
class logserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//server codes
|
||||||
|
static uint8_t OP_SUCCESS;
|
||||||
|
static uint8_t OP_FAIL;
|
||||||
|
static uint8_t OP_SENDING_TUPLE;
|
||||||
|
|
||||||
|
//client codes
|
||||||
|
static uint8_t OP_FIND;
|
||||||
|
static uint8_t OP_INSERT;
|
||||||
|
|
||||||
|
static uint8_t OP_INVALID;
|
||||||
|
|
||||||
|
public:
|
||||||
|
logserver(int nthreads, int server_port){
|
||||||
|
this->nthreads = nthreads;
|
||||||
|
this->server_port = server_port;
|
||||||
|
//lsmlock = new pthread_mutex_t;
|
||||||
|
//pthread_mutex_init(lsmlock,0);
|
||||||
|
|
||||||
|
lsmlock = initlock();
|
||||||
|
|
||||||
|
qlock = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(qlock,0);
|
||||||
|
|
||||||
|
ltable = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~logserver()
|
||||||
|
{
|
||||||
|
//delete lsmlock;
|
||||||
|
deletelock(lsmlock);
|
||||||
|
delete qlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startserver(logtable *ltable);
|
||||||
|
|
||||||
|
void stopserver();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//main loop of server
|
||||||
|
//accept connections, assign jobs to threads
|
||||||
|
void dispatchLoop();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int server_port;
|
||||||
|
|
||||||
|
int nthreads;
|
||||||
|
|
||||||
|
bool sys_alive;
|
||||||
|
|
||||||
|
int serversocket; //server socket file descriptor
|
||||||
|
|
||||||
|
//ccqueue<int> conn_queue; //list of active connections (socket list)
|
||||||
|
|
||||||
|
//ccqueue<pthread_item> idleth_queue; //list of idle threads
|
||||||
|
|
||||||
|
std::queue<int> conn_queue;
|
||||||
|
std::queue<pthread_item> idleth_queue;
|
||||||
|
pthread_mutex_t *qlock;
|
||||||
|
|
||||||
|
std::vector<pthread_item *> th_list; // list of threads
|
||||||
|
|
||||||
|
rwl *lsmlock; //lock for using lsm table
|
||||||
|
|
||||||
|
logtable *ltable;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1606
logstore.cpp
Normal file
1606
logstore.cpp
Normal file
File diff suppressed because it is too large
Load diff
302
logstore.h
Normal file
302
logstore.h
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
#ifndef _LOGSTORE_H_
|
||||||
|
#define _LOGSTORE_H_
|
||||||
|
|
||||||
|
#undef end
|
||||||
|
#undef begin
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "logserver.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stasis/transactional.h>
|
||||||
|
|
||||||
|
#include <stasis/operations.h>
|
||||||
|
#include <stasis/bufferManager.h>
|
||||||
|
#include <stasis/allocationPolicy.h>
|
||||||
|
#include <stasis/blobManager.h>
|
||||||
|
#include <stasis/page.h>
|
||||||
|
#include <stasis/truncation.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "datapage.h"
|
||||||
|
#include "tuplemerger.h"
|
||||||
|
#include "datatuple.h"
|
||||||
|
|
||||||
|
|
||||||
|
double tv_to_double(struct timeval tv);
|
||||||
|
|
||||||
|
|
||||||
|
struct logtable_mergedata;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct RegionAllocConf_t
|
||||||
|
{
|
||||||
|
recordid regionList;
|
||||||
|
pageid_t regionCount;
|
||||||
|
pageid_t nextPage;
|
||||||
|
pageid_t endOfRegion;
|
||||||
|
pageid_t regionSize;
|
||||||
|
} RegionAllocConf_t;
|
||||||
|
|
||||||
|
|
||||||
|
//struct logtree_state {
|
||||||
|
// pageid_t lastLeaf;
|
||||||
|
//};
|
||||||
|
|
||||||
|
|
||||||
|
struct indexnode_rec {
|
||||||
|
pageid_t ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef pageid_t(*logtree_page_allocator_t)(int, void *);
|
||||||
|
typedef void(*logtree_page_deallocator_t)(int, void *);
|
||||||
|
|
||||||
|
|
||||||
|
class logtree{
|
||||||
|
public:
|
||||||
|
logtree();
|
||||||
|
|
||||||
|
recordid create(int xid);
|
||||||
|
|
||||||
|
void print_tree(int xid);
|
||||||
|
|
||||||
|
static pageid_t alloc_region(int xid, void *conf);
|
||||||
|
static pageid_t alloc_region_rid(int xid, void * ridp);
|
||||||
|
static void force_region_rid(int xid, void *conf);
|
||||||
|
static void dealloc_region_rid(int xid, void *conf);
|
||||||
|
static void free_region_rid(int xid, recordid tree,
|
||||||
|
logtree_page_deallocator_t dealloc,
|
||||||
|
void *allocator_state);
|
||||||
|
|
||||||
|
static void writeNodeRecord(int xid, Page *p, recordid &rid,
|
||||||
|
const byte *key, size_t keylen, pageid_t ptr);
|
||||||
|
|
||||||
|
static void writeRecord(int xid, Page *p, recordid &rid,
|
||||||
|
const byte *data, size_t datalen);
|
||||||
|
|
||||||
|
static void writeRecord(int xid, Page *p, slotid_t slot,
|
||||||
|
const byte *data, size_t datalen);
|
||||||
|
|
||||||
|
static const byte* readRecord(int xid, Page * p, recordid &rid);
|
||||||
|
static const byte* readRecord(int xid, Page * p, slotid_t slot, int64_t size);
|
||||||
|
|
||||||
|
static int32_t readRecordLength(int xid, Page *p, slotid_t slot);
|
||||||
|
|
||||||
|
//return the left-most leaf, these are not data pages, although referred to as leaf
|
||||||
|
static pageid_t findFirstLeaf(int xid, Page *root, int64_t depth);
|
||||||
|
//return the right-most leaf
|
||||||
|
static pageid_t findLastLeaf(int xid, Page *root, int64_t depth) ;
|
||||||
|
|
||||||
|
//reads the given record and returns the page id stored in it
|
||||||
|
static pageid_t lookupLeafPageFromRid(int xid, recordid rid);
|
||||||
|
|
||||||
|
//returns a record that stores the pageid where the given key should be in, i.e. if it exists
|
||||||
|
static recordid lookup(int xid, Page *node, int64_t depth, const byte *key,
|
||||||
|
size_t keySize);
|
||||||
|
|
||||||
|
//returns the id of the data page that could contain the given key
|
||||||
|
static pageid_t findPage(int xid, recordid tree, const byte *key, size_t keySize);
|
||||||
|
|
||||||
|
|
||||||
|
//appends a leaf page, val_page is the id of the leaf page
|
||||||
|
//rmLeafID --> rightmost leaf id
|
||||||
|
static recordid appendPage(int xid, recordid tree, pageid_t & rmLeafID,
|
||||||
|
const byte *key,size_t keySize,
|
||||||
|
logtree_page_allocator_t allocator, void *allocator_state,
|
||||||
|
long val_page);
|
||||||
|
|
||||||
|
static recordid appendInternalNode(int xid, Page *p,
|
||||||
|
int64_t depth,
|
||||||
|
const byte *key, size_t key_len,
|
||||||
|
pageid_t val_page, pageid_t lastLeaf,
|
||||||
|
logtree_page_allocator_t allocator,
|
||||||
|
void *allocator_state);
|
||||||
|
|
||||||
|
static recordid buildPathToLeaf(int xid, recordid root, Page *root_p,
|
||||||
|
int64_t depth, const byte *key, size_t key_len,
|
||||||
|
pageid_t val_page, pageid_t lastLeaf,
|
||||||
|
logtree_page_allocator_t allocator,
|
||||||
|
void *allocator_state);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize a page for use as an internal node of the tree.
|
||||||
|
*/
|
||||||
|
inline static void initializeNodePage(int xid, Page *p);
|
||||||
|
|
||||||
|
recordid &get_tree_state(){return tree_state;}
|
||||||
|
recordid &get_root_rec(){return root_rec;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const static RegionAllocConf_t REGION_ALLOC_STATIC_INITIALIZER;
|
||||||
|
const static int64_t DEPTH;
|
||||||
|
const static int64_t COMPARATOR;
|
||||||
|
const static int64_t FIRST_SLOT;
|
||||||
|
const static size_t root_rec_size;
|
||||||
|
const static int64_t PREV_LEAF;
|
||||||
|
const static int64_t NEXT_LEAF;
|
||||||
|
|
||||||
|
pageid_t lastLeaf;
|
||||||
|
private:
|
||||||
|
|
||||||
|
void print_tree(int xid, pageid_t pid, int64_t depth);
|
||||||
|
|
||||||
|
private:
|
||||||
|
recordid tree_state;
|
||||||
|
recordid root_rec;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class logtable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
logtable();
|
||||||
|
~logtable();
|
||||||
|
|
||||||
|
//user access functions
|
||||||
|
datatuple * findTuple(int xid, datatuple::key_t key, size_t keySize);
|
||||||
|
|
||||||
|
datatuple * findTuple_first(int xid, datatuple::key_t key, size_t keySize);
|
||||||
|
|
||||||
|
void insertTuple(struct datatuple &tuple);
|
||||||
|
|
||||||
|
|
||||||
|
//other class functions
|
||||||
|
recordid allocTable(int xid);
|
||||||
|
|
||||||
|
void flushTable();
|
||||||
|
|
||||||
|
DataPage<datatuple>* insertTuple(int xid, struct datatuple &tuple, recordid &dpstate,logtree *ltree);
|
||||||
|
|
||||||
|
datatuple * findTuple(int xid, datatuple::key_t key, size_t keySize, logtree *ltree);
|
||||||
|
|
||||||
|
inline recordid & get_table_rec(){return table_rec;}
|
||||||
|
|
||||||
|
inline logtree * get_tree_c2(){return tree_c2;}
|
||||||
|
inline logtree * get_tree_c1(){return tree_c1;}
|
||||||
|
|
||||||
|
inline void set_tree_c1(logtree *t){tree_c1=t;}
|
||||||
|
inline void set_tree_c2(logtree *t){tree_c2=t;}
|
||||||
|
|
||||||
|
typedef std::set<datatuple, datatuple> rbtree_t;
|
||||||
|
typedef rbtree_t* rbtree_ptr_t;
|
||||||
|
inline rbtree_ptr_t get_tree_c0(){return tree_c0;}
|
||||||
|
|
||||||
|
void set_tree_c0(rbtree_ptr_t newtree){tree_c0 = newtree;}
|
||||||
|
|
||||||
|
inline recordid & get_dpstate1(){return tbl_header.c1_dp_state;}
|
||||||
|
inline recordid & get_dpstate2(){return tbl_header.c2_dp_state;}
|
||||||
|
|
||||||
|
int get_fixed_page_count(){return fixed_page_count;}
|
||||||
|
void set_fixed_page_count(int count){fixed_page_count = count;}
|
||||||
|
|
||||||
|
void setMergeData(logtable_mergedata * mdata) { this->mergedata = mdata;}
|
||||||
|
logtable_mergedata* getMergeData(){return mergedata;}
|
||||||
|
|
||||||
|
inline tuplemerger * gettuplemerger(){return tmerger;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct table_header {
|
||||||
|
recordid c2_root; //tree root record --> points to the root of the b-tree
|
||||||
|
recordid c2_state; //tree state --> describes the regions used by the index tree
|
||||||
|
recordid c2_dp_state; //data pages state --> regions used by the data pages
|
||||||
|
recordid c1_root;
|
||||||
|
recordid c1_state;
|
||||||
|
recordid c1_dp_state;
|
||||||
|
//epoch_t beginning;
|
||||||
|
//epoch_t end;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const static RegionAllocConf_t DATAPAGE_REGION_ALLOC_STATIC_INITIALIZER;
|
||||||
|
|
||||||
|
logtable_mergedata * mergedata;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
recordid table_rec;
|
||||||
|
struct table_header tbl_header;
|
||||||
|
|
||||||
|
logtree *tree_c2; //big tree
|
||||||
|
logtree *tree_c1; //small tree
|
||||||
|
rbtree_ptr_t tree_c0; // in-mem red black tree
|
||||||
|
|
||||||
|
|
||||||
|
int tsize; //number of tuples
|
||||||
|
int64_t tree_bytes; //number of bytes
|
||||||
|
|
||||||
|
|
||||||
|
//DATA PAGE SETTINGS
|
||||||
|
int fixed_page_count;//number of pages in a datapage
|
||||||
|
|
||||||
|
// logtable_mergedata * mergedata;
|
||||||
|
|
||||||
|
tuplemerger *tmerger;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct logtreeIterator_s {
|
||||||
|
Page * p;
|
||||||
|
recordid current;
|
||||||
|
indexnode_rec *t;
|
||||||
|
int justOnePage;
|
||||||
|
} logtreeIterator_s;
|
||||||
|
|
||||||
|
|
||||||
|
class logtreeIterator
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
static lladdIterator_t* open(int xid, recordid root);
|
||||||
|
static lladdIterator_t* openAt(int xid, recordid root, const byte* key);
|
||||||
|
static int next(int xid, lladdIterator_t *it);
|
||||||
|
//static lladdIterator_t *copy(int xid, lladdIterator_t* i);
|
||||||
|
static void close(int xid, lladdIterator_t *it);
|
||||||
|
|
||||||
|
|
||||||
|
static inline int key (int xid, lladdIterator_t *it, byte **key)
|
||||||
|
{
|
||||||
|
logtreeIterator_s * impl = (logtreeIterator_s*)it->impl;
|
||||||
|
*key = (byte*)(impl->t+1);
|
||||||
|
return (int) impl->current.size - sizeof(indexnode_rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int value(int xid, lladdIterator_t *it, byte **value)
|
||||||
|
{
|
||||||
|
logtreeIterator_s * impl = (logtreeIterator_s*)it->impl;
|
||||||
|
*value = (byte*)&(impl->t->ptr);
|
||||||
|
return sizeof(impl->t->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tupleDone(int xid, void *it) { }
|
||||||
|
static inline void releaseLock(int xid, void *it) { }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
836
merger.cpp
Normal file
836
merger.cpp
Normal file
|
@ -0,0 +1,836 @@
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include "merger.h"
|
||||||
|
#include "logiterators.cpp"
|
||||||
|
#include "datapage.cpp"
|
||||||
|
//pageid_t merge_scheduler::C0_MEM_SIZE = 1000 * 1000 * 1000;
|
||||||
|
|
||||||
|
//template <> struct merger_args<rbtree_t>;
|
||||||
|
//template <> struct merger_args<logtree>;
|
||||||
|
inline DataPage<datatuple>*
|
||||||
|
insertTuple(int xid, DataPage<datatuple> *dp, datatuple &t,
|
||||||
|
logtable *ltable,
|
||||||
|
logtree * ltree,
|
||||||
|
recordid & dpstate,
|
||||||
|
int64_t &dpages, int64_t &npages);
|
||||||
|
|
||||||
|
int merge_scheduler::addlogtable(logtable *ltable)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct logtable_mergedata * mdata = new logtable_mergedata;
|
||||||
|
|
||||||
|
// initialize merge data
|
||||||
|
mdata->header_lock = initlock();
|
||||||
|
mdata->rbtree_mut = new pthread_mutex_t;
|
||||||
|
pthread_mutex_init(mdata->rbtree_mut,0);
|
||||||
|
mdata->old_c0 = new rbtree_ptr_t;
|
||||||
|
*mdata->old_c0 = 0;
|
||||||
|
|
||||||
|
mdata->input_needed = new bool(false);
|
||||||
|
|
||||||
|
mdata->input_ready_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(mdata->input_ready_cond,0);
|
||||||
|
|
||||||
|
mdata->input_needed_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(mdata->input_needed_cond,0);
|
||||||
|
|
||||||
|
mdata->input_size = new int64_t(100);
|
||||||
|
|
||||||
|
mdata->diskmerge_args = new merger_args<logtree>;
|
||||||
|
mdata->memmerge_args = new merger_args<rbtree_t>;
|
||||||
|
|
||||||
|
mergedata.push_back(std::make_pair(ltable, mdata));
|
||||||
|
return mergedata.size()-1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_scheduler::~merge_scheduler()
|
||||||
|
{
|
||||||
|
for(int i=0; i<mergedata.size(); i++)
|
||||||
|
{
|
||||||
|
logtable *ltable = mergedata[i].first;
|
||||||
|
logtable_mergedata *mdata = mergedata[i].second;
|
||||||
|
|
||||||
|
//delete the mergedata fields
|
||||||
|
deletelock(mdata->header_lock);
|
||||||
|
delete mdata->rbtree_mut;
|
||||||
|
delete mdata->old_c0;
|
||||||
|
delete mdata->input_needed;
|
||||||
|
delete mdata->input_ready_cond;
|
||||||
|
delete mdata->input_needed_cond;
|
||||||
|
delete mdata->input_size;
|
||||||
|
|
||||||
|
//delete the merge thread structure variables
|
||||||
|
delete (recordid*) mdata->memmerge_args->pageAllocState;
|
||||||
|
delete (recordid*) mdata->memmerge_args->oldAllocState;
|
||||||
|
delete mdata->memmerge_args->still_open;
|
||||||
|
|
||||||
|
delete (recordid*) mdata->diskmerge_args->pageAllocState;
|
||||||
|
delete (recordid*) mdata->diskmerge_args->oldAllocState;
|
||||||
|
|
||||||
|
pthread_cond_destroy(mdata->diskmerge_args->in_block_needed_cond);
|
||||||
|
delete mdata->diskmerge_args->in_block_needed_cond;
|
||||||
|
delete mdata->diskmerge_args->in_block_needed;
|
||||||
|
|
||||||
|
pthread_cond_destroy(mdata->diskmerge_args->out_block_needed_cond);
|
||||||
|
delete mdata->diskmerge_args->out_block_needed_cond;
|
||||||
|
delete mdata->diskmerge_args->out_block_needed;
|
||||||
|
|
||||||
|
pthread_cond_destroy(mdata->diskmerge_args->in_block_ready_cond);
|
||||||
|
delete mdata->diskmerge_args->in_block_ready_cond;
|
||||||
|
pthread_cond_destroy(mdata->diskmerge_args->out_block_ready_cond);
|
||||||
|
delete mdata->diskmerge_args->out_block_ready_cond;
|
||||||
|
|
||||||
|
delete mdata->diskmerge_args->my_tree_size;
|
||||||
|
|
||||||
|
delete mdata->diskmerge_args;
|
||||||
|
delete mdata->memmerge_args;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
mergedata.clear();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge_scheduler::shutdown()
|
||||||
|
{
|
||||||
|
//signal shutdown
|
||||||
|
for(int i=0; i<mergedata.size(); i++)
|
||||||
|
{
|
||||||
|
logtable *ltable = mergedata[i].first;
|
||||||
|
logtable_mergedata *mdata = mergedata[i].second;
|
||||||
|
|
||||||
|
//flush the in memory table to write any tuples still in memory
|
||||||
|
ltable->flushTable();
|
||||||
|
|
||||||
|
pthread_mutex_lock(mdata->rbtree_mut);
|
||||||
|
*(mdata->memmerge_args->still_open)=false;
|
||||||
|
pthread_cond_signal(mdata->input_ready_cond);
|
||||||
|
|
||||||
|
//*(mdata->diskmerge_args->still_open)=false;//same pointer so no need
|
||||||
|
|
||||||
|
pthread_mutex_unlock(mdata->rbtree_mut);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<mergedata.size(); i++)
|
||||||
|
{
|
||||||
|
logtable_mergedata *mdata = mergedata[i].second;
|
||||||
|
|
||||||
|
pthread_join(mdata->memmerge_thread,0);
|
||||||
|
pthread_join(mdata->diskmerge_thread,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge_scheduler::startlogtable(int index)
|
||||||
|
{
|
||||||
|
logtable * ltable = mergedata[index].first;
|
||||||
|
struct logtable_mergedata *mdata = mergedata[index].second;
|
||||||
|
|
||||||
|
pthread_cond_t * block1_needed_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(block1_needed_cond,0);
|
||||||
|
pthread_cond_t * block2_needed_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(block2_needed_cond,0);
|
||||||
|
|
||||||
|
pthread_cond_t * block1_ready_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(block1_ready_cond,0);
|
||||||
|
pthread_cond_t * block2_ready_cond = new pthread_cond_t;
|
||||||
|
pthread_cond_init(block2_ready_cond,0);
|
||||||
|
|
||||||
|
bool *block1_needed = new bool(false);
|
||||||
|
bool *block2_needed = new bool(false);
|
||||||
|
bool *system_running = new bool(true);
|
||||||
|
|
||||||
|
//wait to merge the next block until we have merged block FUDGE times.
|
||||||
|
static const int FUDGE = 1;
|
||||||
|
static double R = MIN_R;
|
||||||
|
int64_t * block1_size = new int64_t;
|
||||||
|
*block1_size = FUDGE * ((int)R) * (*(mdata->input_size));
|
||||||
|
|
||||||
|
//initialize rb-tree
|
||||||
|
ltable->set_tree_c0(new rbtree_t);
|
||||||
|
|
||||||
|
//disk merger args
|
||||||
|
recordid * ridp = new recordid;
|
||||||
|
*ridp = ltable->get_tree_c2()->get_tree_state(); //h.bigTreeAllocState;
|
||||||
|
recordid * oldridp = new recordid;
|
||||||
|
*oldridp = NULLRID;
|
||||||
|
|
||||||
|
logtree ** block1_scratch = new logtree*;
|
||||||
|
*block1_scratch=0;
|
||||||
|
|
||||||
|
//recordid * allocer_scratch = new recordid;
|
||||||
|
RegionAllocConf_t *allocer_scratch = new RegionAllocConf_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct merger_args<logtree> diskmerge_args= {
|
||||||
|
ltable,
|
||||||
|
1, //worker id
|
||||||
|
logtree::alloc_region_rid, //pageAlloc
|
||||||
|
ridp, // pageAllocState
|
||||||
|
oldridp, // oldAllocState
|
||||||
|
mdata->rbtree_mut, //block_ready_mutex
|
||||||
|
block1_needed_cond, //in_block_needed_cond
|
||||||
|
block1_needed, //in_block_needed
|
||||||
|
block2_needed_cond, //out_block_needed_cond
|
||||||
|
block2_needed, //out_block_needed
|
||||||
|
block1_ready_cond, //in_block_ready_cond
|
||||||
|
block2_ready_cond, //out_block_ready_cond
|
||||||
|
system_running, //still_open i.e. system running
|
||||||
|
block1_size, //mytree_size ?
|
||||||
|
0, //out_tree_size, biggest component computes its size directly.
|
||||||
|
0, //max_tree_size No max size for biggest component
|
||||||
|
&R, //r_i
|
||||||
|
block1_scratch, //in-tree
|
||||||
|
allocer_scratch, //in_tree_allocer
|
||||||
|
0, //out_tree
|
||||||
|
0, //out_tree_allocer
|
||||||
|
new treeIterator<datatuple>::treeIteratorHandle(ltable->get_tree_c2()->get_root_rec()), // my_tree
|
||||||
|
ltable->get_table_rec() //tree
|
||||||
|
};
|
||||||
|
|
||||||
|
*mdata->diskmerge_args = diskmerge_args;
|
||||||
|
|
||||||
|
DEBUG("Tree C2 is %lld\n", (long long)ltable->get_tree_c2()->get_root_rec().page);
|
||||||
|
|
||||||
|
|
||||||
|
//memory merger args
|
||||||
|
ridp = new recordid;
|
||||||
|
*ridp = ltable->get_tree_c1()->get_tree_state();
|
||||||
|
oldridp = new recordid;
|
||||||
|
*oldridp = NULLRID;
|
||||||
|
|
||||||
|
DEBUG("Tree C1 is %lld\n", (long long)ltable->get_tree_c1()->get_root_rec().page);
|
||||||
|
|
||||||
|
struct merger_args<rbtree_t> memmerge_args =
|
||||||
|
{
|
||||||
|
ltable,
|
||||||
|
2,
|
||||||
|
logtree::alloc_region_rid, //pageAlloc
|
||||||
|
ridp, // pageAllocState
|
||||||
|
oldridp, // oldAllocState
|
||||||
|
mdata->rbtree_mut, //block_ready_mutex
|
||||||
|
mdata->input_needed_cond,
|
||||||
|
mdata->input_needed,
|
||||||
|
block1_needed_cond,
|
||||||
|
block1_needed,
|
||||||
|
mdata->input_ready_cond,
|
||||||
|
block1_ready_cond,
|
||||||
|
system_running,
|
||||||
|
mdata->input_size,
|
||||||
|
block1_size,
|
||||||
|
(int64_t)(R * R * MAX_C0_SIZE),
|
||||||
|
&R,
|
||||||
|
mdata->old_c0,
|
||||||
|
0,
|
||||||
|
block1_scratch,
|
||||||
|
allocer_scratch,
|
||||||
|
new treeIterator<datatuple>::treeIteratorHandle(ltable->get_tree_c1()->get_root_rec()),
|
||||||
|
ltable->get_table_rec() //tree
|
||||||
|
};
|
||||||
|
|
||||||
|
*mdata->memmerge_args = memmerge_args;
|
||||||
|
|
||||||
|
void * (*diskmerger)(void*) = diskMergeThread;
|
||||||
|
void * (*memmerger)(void*) = memMergeThread;
|
||||||
|
|
||||||
|
pthread_create(&mdata->diskmerge_thread, 0, diskmerger, mdata->diskmerge_args);
|
||||||
|
pthread_create(&mdata->memmerge_thread, 0, memmerger, mdata->memmerge_args);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: flush the data pages
|
||||||
|
// deallocate/free their region
|
||||||
|
// create new data region for new data pages
|
||||||
|
void* memMergeThread(void*arg)
|
||||||
|
{
|
||||||
|
|
||||||
|
int xid;// = Tbegin();
|
||||||
|
|
||||||
|
merger_args<rbtree_t> * a = (merger_args<rbtree_t>*)(arg);
|
||||||
|
assert(a->my_tree->r_.size != -1);
|
||||||
|
|
||||||
|
logtable * ltable = a->ltable;
|
||||||
|
|
||||||
|
int merge_count =0;
|
||||||
|
// pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
int done = 0;
|
||||||
|
// get a new input for merge
|
||||||
|
while(!*(a->in_tree))
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
*a->in_block_needed = true;
|
||||||
|
//pthread_cond_signal(a->in_block_needed_cond);
|
||||||
|
pthread_cond_broadcast(a->in_block_needed_cond);
|
||||||
|
|
||||||
|
if(!*(a->still_open)){
|
||||||
|
done = 1;
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("mmt:\twaiting for block ready cond\n");
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
pthread_cond_wait(a->in_block_ready_cond, a->block_ready_mut);
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
printf("mmt:\tblock ready\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
*a->in_block_needed = false;
|
||||||
|
|
||||||
|
if(done==1)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
pthread_cond_signal(a->out_block_ready_cond);
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((*a->in_tree)->size()==0) //input empty, this can only happen during shutdown
|
||||||
|
{
|
||||||
|
delete *a->in_tree;
|
||||||
|
*a->in_tree = 0;
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t insertedTuples=0;
|
||||||
|
int64_t mergedPages=0;
|
||||||
|
|
||||||
|
assert(a->my_tree->r_.size != -1);
|
||||||
|
|
||||||
|
//create the iterators
|
||||||
|
treeIterator<datatuple> *itrA = new treeIterator<datatuple>(a->my_tree->r_);
|
||||||
|
memTreeIterator<rbtree_t, datatuple> *itrB =
|
||||||
|
new memTreeIterator<rbtree_t, datatuple>(*a->in_tree);
|
||||||
|
memTreeIterator<rbtree_t, datatuple> *itrBend = itrB->end();
|
||||||
|
|
||||||
|
//Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
//create a new tree
|
||||||
|
logtree * scratch_tree = new logtree;
|
||||||
|
recordid scratch_root = scratch_tree->create(xid);
|
||||||
|
|
||||||
|
//save the old dp state values
|
||||||
|
RegionAllocConf_t olddp_state;
|
||||||
|
Tread(xid, ltable->get_dpstate1(), &olddp_state);
|
||||||
|
//reinitialize the dp state
|
||||||
|
Tset(xid, ltable->get_dpstate1(), &logtable::DATAPAGE_REGION_ALLOC_STATIC_INITIALIZER);
|
||||||
|
|
||||||
|
//pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
//: do the merge
|
||||||
|
printf("mmt:\tMerging:\n");
|
||||||
|
|
||||||
|
int64_t npages = 0;
|
||||||
|
mergedPages = merge_iterators(xid, itrA, itrB, ltable, scratch_tree, npages);
|
||||||
|
|
||||||
|
delete itrA;
|
||||||
|
delete itrB;
|
||||||
|
delete itrBend;
|
||||||
|
|
||||||
|
//force write the new region to disk
|
||||||
|
recordid scratch_alloc_state = scratch_tree->get_tree_state();
|
||||||
|
//TlsmForce(xid,scratch_root,logtree::force_region_rid, &scratch_alloc_state);
|
||||||
|
logtree::force_region_rid(xid, &scratch_alloc_state);
|
||||||
|
//force write the new datapages
|
||||||
|
DataPage<datatuple>::force_region_rid(xid, <able->get_dpstate1());
|
||||||
|
|
||||||
|
//writes complete
|
||||||
|
//now automically replace the old c1 with new c1
|
||||||
|
//pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
merge_count++;
|
||||||
|
*a->my_tree_size = mergedPages;
|
||||||
|
printf("mmt:\tmerge_count %d #pages written %lld\n", merge_count, npages);
|
||||||
|
|
||||||
|
delete ltable->get_tree_c1();
|
||||||
|
ltable->set_tree_c1(scratch_tree);
|
||||||
|
|
||||||
|
logtable::table_header h;
|
||||||
|
void * oldAllocState = a->pageAllocState;
|
||||||
|
Tread(xid, a->tree, &h);
|
||||||
|
|
||||||
|
h.c1_root = scratch_root;
|
||||||
|
h.c1_state = scratch_alloc_state;
|
||||||
|
//note we already updated the dpstate before the merge
|
||||||
|
printf("mmt:\tUpdated C1's position on disk to %lld\n",scratch_root.page);
|
||||||
|
Tset(xid, a->tree, &h);
|
||||||
|
|
||||||
|
//Tcommit(xid);
|
||||||
|
//xid = Tbegin();
|
||||||
|
|
||||||
|
// free old my_tree here
|
||||||
|
//TODO: check
|
||||||
|
logtree::free_region_rid(xid, a->my_tree->r_, logtree::dealloc_region_rid, oldAllocState);
|
||||||
|
|
||||||
|
|
||||||
|
//TlsmFree(xid,a->my_tree->r_,logtree::dealloc_region_rid,oldAllocState);
|
||||||
|
//TODO: check
|
||||||
|
//free the old data pages
|
||||||
|
DataPage<datatuple>::dealloc_region_rid(xid, &olddp_state);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
//xid = Tbegin();
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: this is simplistic for now
|
||||||
|
//signal the other merger if necessary
|
||||||
|
double target_R = *(a->r_i);
|
||||||
|
double new_c1_size = npages * PAGE_SIZE;
|
||||||
|
assert(target_R >= MIN_R);
|
||||||
|
if( (new_c1_size / MAX_C0_SIZE > target_R) ||
|
||||||
|
(a->max_size && new_c1_size > a->max_size ) )
|
||||||
|
{
|
||||||
|
printf("mmt:\tsignaling C2 for merge\n");
|
||||||
|
printf("mmt:\tnew_c1_size %.2f\tMAX_C0_SIZE %lld\ta->max_size %lld\t targetr %.2f \n", new_c1_size,
|
||||||
|
MAX_C0_SIZE, a->max_size, target_R);
|
||||||
|
|
||||||
|
// XXX need to report backpressure here!
|
||||||
|
while(*a->out_tree) {
|
||||||
|
pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
pthread_cond_wait(a->out_block_needed_cond, a->block_ready_mut);
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*a->out_tree = scratch_tree;
|
||||||
|
xid = Tbegin();
|
||||||
|
Tread(xid, ltable->get_dpstate1(), a->out_tree_allocer);
|
||||||
|
|
||||||
|
pthread_cond_signal(a->out_block_ready_cond);
|
||||||
|
|
||||||
|
|
||||||
|
logtree *empty_tree = new logtree;
|
||||||
|
empty_tree->create(xid);
|
||||||
|
|
||||||
|
*(recordid*)(a->pageAllocState) = empty_tree->get_tree_state();
|
||||||
|
|
||||||
|
a->my_tree->r_ = empty_tree->get_root_rec();
|
||||||
|
|
||||||
|
ltable->set_tree_c1(empty_tree);
|
||||||
|
|
||||||
|
logtable::table_header h;
|
||||||
|
Tread(xid, a->tree, &h);
|
||||||
|
h.c1_root = empty_tree->get_root_rec(); //update root
|
||||||
|
h.c1_state = empty_tree->get_tree_state(); //update index alloc state
|
||||||
|
printf("mmt:\tUpdated C1's position on disk to %lld\n",empty_tree->get_root_rec().page);
|
||||||
|
Tset(xid, a->tree, &h);
|
||||||
|
//update datapage alloc state
|
||||||
|
Tset(xid, ltable->get_dpstate1(), &logtable::DATAPAGE_REGION_ALLOC_STATIC_INITIALIZER);
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
//xid = Tbegin();
|
||||||
|
|
||||||
|
}
|
||||||
|
else //not signaling the C2 for merge yet
|
||||||
|
{
|
||||||
|
printf("mmt:\tnot signaling C2 for merge\n");
|
||||||
|
*(recordid*)a->pageAllocState = scratch_alloc_state;
|
||||||
|
a->my_tree->r_ = scratch_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
rbtree_ptr_t deltree = *a->in_tree;
|
||||||
|
*a->in_tree = 0;
|
||||||
|
|
||||||
|
|
||||||
|
//Tcommit(xid);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
//TODO: get the freeing outside of the lock
|
||||||
|
//// ----------- Free in_tree
|
||||||
|
for(rbtree_t::iterator delitr=deltree->begin();
|
||||||
|
delitr != deltree->end(); delitr++)
|
||||||
|
free((*delitr).keylen);
|
||||||
|
|
||||||
|
delete deltree;
|
||||||
|
//deltree = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(rbtree_t::iterator delitr=(*a->in_tree)->begin();
|
||||||
|
delitr != (*a->in_tree)->end(); delitr++)
|
||||||
|
free((*delitr).keylen);
|
||||||
|
|
||||||
|
delete *a->in_tree;
|
||||||
|
*a->in_tree = 0;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *diskMergeThread(void*arg)
|
||||||
|
{
|
||||||
|
int xid;// = Tbegin();
|
||||||
|
|
||||||
|
merger_args<logtree> * a = (merger_args<logtree>*)(arg);
|
||||||
|
assert(a->my_tree->r_.size != -1);
|
||||||
|
|
||||||
|
logtable * ltable = a->ltable;
|
||||||
|
|
||||||
|
int merge_count =0;
|
||||||
|
//pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
int done = 0;
|
||||||
|
// get a new input for merge
|
||||||
|
while(!*(a->in_tree))
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
*a->in_block_needed = true;
|
||||||
|
pthread_cond_signal(a->in_block_needed_cond);
|
||||||
|
|
||||||
|
if(!*(a->still_open)){
|
||||||
|
done = 1;
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("dmt:\twaiting for block ready cond\n");
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
pthread_cond_wait(a->in_block_ready_cond, a->block_ready_mut);
|
||||||
|
pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
|
||||||
|
printf("dmt:\tblock ready\n");
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
}
|
||||||
|
*a->in_block_needed = false;
|
||||||
|
if(done==1)
|
||||||
|
{
|
||||||
|
pthread_cond_signal(a->out_block_ready_cond);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t insertedTuples=0;
|
||||||
|
int64_t mergedPages=0;
|
||||||
|
|
||||||
|
assert(a->my_tree->r_.size != -1);
|
||||||
|
|
||||||
|
//create the iterators
|
||||||
|
treeIterator<datatuple> *itrA = new treeIterator<datatuple>(a->my_tree->r_);
|
||||||
|
treeIterator<datatuple> *itrB =
|
||||||
|
new treeIterator<datatuple>((*a->in_tree)->get_root_rec());
|
||||||
|
|
||||||
|
//Tcommit(xid);
|
||||||
|
xid = Tbegin();
|
||||||
|
|
||||||
|
//create a new tree
|
||||||
|
logtree * scratch_tree = new logtree;
|
||||||
|
recordid scratch_root = scratch_tree->create(xid);
|
||||||
|
|
||||||
|
//save the old dp state values
|
||||||
|
RegionAllocConf_t olddp_state;
|
||||||
|
Tread(xid, ltable->get_dpstate2(), &olddp_state);
|
||||||
|
//reinitialize the dp state
|
||||||
|
//TODO: maybe you want larger regions for the second tree?
|
||||||
|
Tset(xid, ltable->get_dpstate2(), &logtable::DATAPAGE_REGION_ALLOC_STATIC_INITIALIZER);
|
||||||
|
|
||||||
|
//pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
|
||||||
|
//do the merge
|
||||||
|
printf("dmt:\tMerging:\n");
|
||||||
|
|
||||||
|
int64_t npages = 0;
|
||||||
|
mergedPages = merge_iterators(xid, itrA, itrB, ltable, scratch_tree, npages);
|
||||||
|
|
||||||
|
delete itrA;
|
||||||
|
delete itrB;
|
||||||
|
|
||||||
|
//force write the new region to disk
|
||||||
|
recordid scratch_alloc_state = scratch_tree->get_tree_state();
|
||||||
|
//TODO:
|
||||||
|
//TlsmForce(xid,scratch_root,logtree::force_region_rid, &scratch_alloc_state);
|
||||||
|
logtree::force_region_rid(xid, &scratch_alloc_state);
|
||||||
|
//force write the new datapages
|
||||||
|
DataPage<datatuple>::force_region_rid(xid, <able->get_dpstate2());
|
||||||
|
|
||||||
|
|
||||||
|
//writes complete
|
||||||
|
//now automically replace the old c2 with new c2
|
||||||
|
//pthread_mutex_lock(a->block_ready_mut);
|
||||||
|
writelock(ltable->mergedata->header_lock,0);
|
||||||
|
|
||||||
|
merge_count++;
|
||||||
|
*a->my_tree_size = mergedPages;
|
||||||
|
//update the current optimal R value
|
||||||
|
*(a->r_i) = std::max(MIN_R, sqrt( (npages * 1.0) / (MAX_C0_SIZE/PAGE_SIZE) ) );
|
||||||
|
|
||||||
|
printf("dmt:\tmerge_count %d\t#written pages: %lld\n optimal r %.2f", merge_count, npages, *(a->r_i));
|
||||||
|
|
||||||
|
delete ltable->get_tree_c2();
|
||||||
|
ltable->set_tree_c2(scratch_tree);
|
||||||
|
|
||||||
|
logtable::table_header h;
|
||||||
|
void * oldAllocState = a->pageAllocState;
|
||||||
|
Tread(xid, a->tree, &h);
|
||||||
|
|
||||||
|
h.c2_root = scratch_root;
|
||||||
|
h.c2_state = scratch_alloc_state;
|
||||||
|
//note we already updated the dpstate before the merge
|
||||||
|
printf("dmt:\tUpdated C2's position on disk to %lld\n",scratch_root.page);
|
||||||
|
Tset(xid, a->tree, &h);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// free old my_tree here
|
||||||
|
//TODO: check
|
||||||
|
logtree::free_region_rid(xid, a->my_tree->r_, logtree::dealloc_region_rid, oldAllocState);
|
||||||
|
//TlsmFree(xid,a->my_tree->r_,logtree::dealloc_region_rid,oldAllocState);
|
||||||
|
|
||||||
|
//TODO: check
|
||||||
|
//free the old data pages
|
||||||
|
DataPage<datatuple>::dealloc_region_rid(xid, &olddp_state);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*(recordid*)a->pageAllocState = scratch_alloc_state;
|
||||||
|
a->my_tree->r_ = scratch_root;
|
||||||
|
|
||||||
|
//// ----------- Free in_tree
|
||||||
|
//TODO: check
|
||||||
|
logtree::free_region_rid(xid, (*a->in_tree)->get_root_rec(),
|
||||||
|
logtree::dealloc_region_rid,
|
||||||
|
&((*a->in_tree)->get_tree_state()));
|
||||||
|
//TlsmFree(xid,a->my_tree->r_,logtree::dealloc_region_rid,oldAllocState);
|
||||||
|
|
||||||
|
//TODO: check
|
||||||
|
//free the old data pages
|
||||||
|
DataPage<datatuple>::dealloc_region_rid(xid, a->in_tree_allocer);//TODO:
|
||||||
|
|
||||||
|
Tcommit(xid);
|
||||||
|
|
||||||
|
//xid = Tbegin();
|
||||||
|
//Tcommit(xid);
|
||||||
|
delete *a->in_tree;
|
||||||
|
*a->in_tree = 0;
|
||||||
|
|
||||||
|
unlock(ltable->mergedata->header_lock);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//pthread_mutex_unlock(a->block_ready_mut);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t merge_iterators(int xid,
|
||||||
|
treeIterator<datatuple> *itrA,
|
||||||
|
memTreeIterator<rbtree_t, datatuple> * itrB,
|
||||||
|
logtable *ltable,
|
||||||
|
logtree *scratch_tree,
|
||||||
|
int64_t &npages )
|
||||||
|
{
|
||||||
|
int64_t dpages = 0;
|
||||||
|
//int npages = 0;
|
||||||
|
int64_t ntuples = 0;
|
||||||
|
DataPage<datatuple> *dp = 0;
|
||||||
|
|
||||||
|
memTreeIterator<rbtree_t, datatuple> *itrBend = itrB->end();
|
||||||
|
datatuple *t1 = itrA->getnext();
|
||||||
|
|
||||||
|
while(*itrB != *itrBend)
|
||||||
|
{
|
||||||
|
datatuple t2 = **itrB;
|
||||||
|
DEBUG("tuple\t%lld: keylen %d datalen %d\n", ntuples, *t2.keylen,*t2.datalen );
|
||||||
|
|
||||||
|
while(t1 != 0 && datatuple::compare(t1->key, t2.key) < 0) // t1 is less than t2
|
||||||
|
{
|
||||||
|
//insert t1
|
||||||
|
dp = insertTuple(xid, dp, *t1, ltable, scratch_tree, ltable->get_dpstate1(),
|
||||||
|
dpages, npages);
|
||||||
|
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
ntuples++;
|
||||||
|
//advance itrA
|
||||||
|
t1 = itrA->getnext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t1 != 0 && datatuple::compare(t1->key, t2.key) == 0)
|
||||||
|
{
|
||||||
|
datatuple *mtuple = ltable->gettuplemerger()->merge(t1,&t2);
|
||||||
|
//insert merged tuple
|
||||||
|
dp = insertTuple(xid, dp, *mtuple, ltable, scratch_tree, ltable->get_dpstate1(),
|
||||||
|
dpages, npages);
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
t1 = itrA->getnext(); //advance itrA
|
||||||
|
free(mtuple->keylen);
|
||||||
|
free(mtuple);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//insert t2
|
||||||
|
dp = insertTuple(xid, dp, t2, ltable, scratch_tree, ltable->get_dpstate1(),
|
||||||
|
dpages, npages);
|
||||||
|
//free(t2.keylen); //cannot free here it may still be read through a lookup
|
||||||
|
}
|
||||||
|
|
||||||
|
ntuples++;
|
||||||
|
++(*itrB);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(t1 != 0) // t1 is less than t2
|
||||||
|
{
|
||||||
|
dp = insertTuple(xid, dp, *t1, ltable, scratch_tree, ltable->get_dpstate1(),
|
||||||
|
dpages, npages);
|
||||||
|
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
ntuples++;
|
||||||
|
//advance itrA
|
||||||
|
t1 = itrA->getnext();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete itrBend;
|
||||||
|
if(dp!=NULL)
|
||||||
|
delete dp;
|
||||||
|
DEBUG("dpages: %d\tnpages: %d\tntuples: %d\n", dpages, npages, ntuples);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
|
||||||
|
return dpages;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t merge_iterators(int xid,
|
||||||
|
treeIterator<datatuple> *itrA, //iterator on c2
|
||||||
|
treeIterator<datatuple> *itrB, //iterator on c1
|
||||||
|
logtable *ltable,
|
||||||
|
logtree *scratch_tree,
|
||||||
|
int64_t &npages)
|
||||||
|
{
|
||||||
|
int64_t dpages = 0;
|
||||||
|
//int npages = 0;
|
||||||
|
int64_t ntuples = 0;
|
||||||
|
DataPage<datatuple> *dp = 0;
|
||||||
|
|
||||||
|
datatuple *t1 = itrA->getnext();
|
||||||
|
datatuple *t2 = 0;
|
||||||
|
|
||||||
|
while( (t2=itrB->getnext()) != 0)
|
||||||
|
{
|
||||||
|
DEBUG("tuple\t%lld: keylen %d datalen %d\n",
|
||||||
|
ntuples, *(t2->keylen),*(t2->datalen) );
|
||||||
|
|
||||||
|
while(t1 != 0 && datatuple::compare(t1->key, t2->key) < 0) // t1 is less than t2
|
||||||
|
{
|
||||||
|
//insert t1
|
||||||
|
dp = insertTuple(xid, dp, *t1, ltable, scratch_tree,
|
||||||
|
ltable->get_dpstate2(),
|
||||||
|
dpages, npages);
|
||||||
|
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
ntuples++;
|
||||||
|
//advance itrA
|
||||||
|
t1 = itrA->getnext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t1 != 0 && datatuple::compare(t1->key, t2->key) == 0)
|
||||||
|
{
|
||||||
|
datatuple *mtuple = ltable->gettuplemerger()->merge(t1,t2);
|
||||||
|
|
||||||
|
//insert merged tuple, drop deletes
|
||||||
|
if(!mtuple->isDelete())
|
||||||
|
dp = insertTuple(xid, dp, *mtuple, ltable, scratch_tree, ltable->get_dpstate2(),
|
||||||
|
dpages, npages);
|
||||||
|
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
t1 = itrA->getnext(); //advance itrA
|
||||||
|
free(mtuple->keylen);
|
||||||
|
free(mtuple);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//insert t2
|
||||||
|
dp = insertTuple(xid, dp, *t2, ltable, scratch_tree, ltable->get_dpstate2(),
|
||||||
|
dpages, npages);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(t2->keylen);
|
||||||
|
free(t2);
|
||||||
|
ntuples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(t1 != 0)
|
||||||
|
{
|
||||||
|
dp = insertTuple(xid, dp, *t1, ltable, scratch_tree, ltable->get_dpstate2(),
|
||||||
|
dpages, npages);
|
||||||
|
|
||||||
|
free(t1->keylen);
|
||||||
|
free(t1);
|
||||||
|
ntuples++;
|
||||||
|
//advance itrA
|
||||||
|
t1 = itrA->getnext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dp!=NULL)
|
||||||
|
delete dp;
|
||||||
|
DEBUG("dpages: %d\tnpages: %d\tntuples: %d\n", dpages, npages, ntuples);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
return dpages;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline DataPage<datatuple>*
|
||||||
|
insertTuple(int xid, DataPage<datatuple> *dp, datatuple &t,
|
||||||
|
logtable *ltable,
|
||||||
|
logtree * ltree,
|
||||||
|
recordid & dpstate,
|
||||||
|
int64_t &dpages, int64_t &npages)
|
||||||
|
{
|
||||||
|
if(dp==0)
|
||||||
|
{
|
||||||
|
dp = ltable->insertTuple(xid, t, dpstate, ltree);
|
||||||
|
dpages++;
|
||||||
|
}
|
||||||
|
else if(!dp->append(xid, t))
|
||||||
|
{
|
||||||
|
npages += dp->get_page_count();
|
||||||
|
delete dp;
|
||||||
|
dp = ltable->insertTuple(xid, t, dpstate, ltree);
|
||||||
|
dpages++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
127
merger.h
Normal file
127
merger.h
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#ifndef _MERGER_H_
|
||||||
|
#define _MERGER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "logstore.h"
|
||||||
|
#include "logiterators.h"
|
||||||
|
|
||||||
|
typedef std::set<datatuple, datatuple> rbtree_t;
|
||||||
|
typedef rbtree_t* rbtree_ptr_t;
|
||||||
|
|
||||||
|
//TODO: 400 bytes overhead per tuple, this is nuts, check if this is true...
|
||||||
|
static const int RB_TREE_OVERHEAD = 400;
|
||||||
|
static const int64_t MAX_C0_SIZE = 800 *1024*1024; //max size of c0
|
||||||
|
static const double MIN_R = 3.0;
|
||||||
|
//T is either logtree or red-black tree
|
||||||
|
template <class T>
|
||||||
|
struct merger_args
|
||||||
|
{
|
||||||
|
logtable * ltable;
|
||||||
|
int worker_id;
|
||||||
|
|
||||||
|
//page allocation information
|
||||||
|
pageid_t(*pageAlloc)(int,void*);
|
||||||
|
void *pageAllocState;
|
||||||
|
void *oldAllocState;
|
||||||
|
|
||||||
|
pthread_mutex_t * block_ready_mut;
|
||||||
|
|
||||||
|
pthread_cond_t * in_block_needed_cond;
|
||||||
|
bool * in_block_needed;
|
||||||
|
|
||||||
|
pthread_cond_t * out_block_needed_cond;
|
||||||
|
bool * out_block_needed;
|
||||||
|
|
||||||
|
pthread_cond_t * in_block_ready_cond;
|
||||||
|
pthread_cond_t * out_block_ready_cond;
|
||||||
|
|
||||||
|
bool * still_open;
|
||||||
|
|
||||||
|
int64_t * my_tree_size;
|
||||||
|
int64_t * out_tree_size;
|
||||||
|
int64_t max_size; //pageid_t
|
||||||
|
double * r_i;
|
||||||
|
|
||||||
|
T ** in_tree;
|
||||||
|
void * in_tree_allocer;
|
||||||
|
|
||||||
|
logtree ** out_tree;
|
||||||
|
void * out_tree_allocer;
|
||||||
|
|
||||||
|
treeIterator<datatuple>::treeIteratorHandle *my_tree;
|
||||||
|
|
||||||
|
recordid tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct logtable_mergedata
|
||||||
|
{
|
||||||
|
//merge threads
|
||||||
|
pthread_t diskmerge_thread;
|
||||||
|
pthread_t memmerge_thread;
|
||||||
|
|
||||||
|
rwl *header_lock;
|
||||||
|
|
||||||
|
pthread_mutex_t * rbtree_mut;
|
||||||
|
rbtree_ptr_t *old_c0; //in-mem red black tree being merged / to be merged
|
||||||
|
|
||||||
|
bool *input_needed; // memmerge-input needed
|
||||||
|
|
||||||
|
pthread_cond_t * input_ready_cond;
|
||||||
|
pthread_cond_t * input_needed_cond;
|
||||||
|
int64_t * input_size;
|
||||||
|
|
||||||
|
//merge args 1
|
||||||
|
struct merger_args<logtree> *diskmerge_args;
|
||||||
|
//merge args 2
|
||||||
|
struct merger_args<rbtree_t> *memmerge_args;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class merge_scheduler
|
||||||
|
{
|
||||||
|
std::vector<std::pair<logtable *, logtable_mergedata*> > mergedata;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//static pageid_t C0_MEM_SIZE;
|
||||||
|
~merge_scheduler();
|
||||||
|
|
||||||
|
int addlogtable(logtable * ltable);
|
||||||
|
void startlogtable(int index);
|
||||||
|
|
||||||
|
struct logtable_mergedata *getMergeData(int index){return mergedata[index].second;}
|
||||||
|
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void* memMergeThread(void* arg);
|
||||||
|
|
||||||
|
//merges and returns the number of data pages used
|
||||||
|
int64_t merge_iterators(int xid,
|
||||||
|
treeIterator<datatuple> *itrA,
|
||||||
|
memTreeIterator<rbtree_t, datatuple> * itrB,
|
||||||
|
logtable *ltable,
|
||||||
|
logtree *scratch_tree,
|
||||||
|
int64_t &npages);
|
||||||
|
|
||||||
|
|
||||||
|
int64_t merge_iterators(int xid,
|
||||||
|
treeIterator<datatuple> *itrA,
|
||||||
|
treeIterator<datatuple> *itrB,
|
||||||
|
logtable *ltable,
|
||||||
|
logtree *scratch_tree,
|
||||||
|
int64_t &npages);
|
||||||
|
|
||||||
|
|
||||||
|
void* diskMergeThread(void* arg);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
84
tuplemerger.cpp
Normal file
84
tuplemerger.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include "tuplemerger.h"
|
||||||
|
#include "logstore.h"
|
||||||
|
|
||||||
|
datatuple* tuplemerger::merge(datatuple *t1, datatuple *t2)
|
||||||
|
{
|
||||||
|
assert(!t1->isDelete() || !t2->isDelete()); //both cannot be delete
|
||||||
|
|
||||||
|
datatuple *t;
|
||||||
|
|
||||||
|
if(t1->isDelete()) //delete - t2
|
||||||
|
{
|
||||||
|
t = datatuple::from_bytes(t2->to_bytes());
|
||||||
|
}
|
||||||
|
else if(t2->isDelete())
|
||||||
|
{
|
||||||
|
t = datatuple::from_bytes(t2->to_bytes());
|
||||||
|
}
|
||||||
|
else //neither is a delete
|
||||||
|
{
|
||||||
|
t = (*merge_fp)(t1,t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* appends the data in t2 to data from t1
|
||||||
|
*
|
||||||
|
* deletes are handled by the tuplemerger::merge function
|
||||||
|
* so here neither t1 nor t2 is a delete datatuple
|
||||||
|
**/
|
||||||
|
datatuple* append_merger(datatuple *t1, datatuple *t2)
|
||||||
|
{
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
struct datatuple *t = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
|
||||||
|
byte *arr = (byte*)malloc(t1->byte_length() + *t2->datalen);
|
||||||
|
|
||||||
|
t->keylen = (uint32_t*) arr;
|
||||||
|
*(t->keylen) = *(t1->keylen);
|
||||||
|
|
||||||
|
t->datalen = (uint32_t*) (arr+isize);
|
||||||
|
*(t->datalen) = *(t1->datalen) + *(t2->datalen);
|
||||||
|
|
||||||
|
t->key = (datatuple::key_t) (arr+isize+isize);
|
||||||
|
memcpy((byte*)t->key, (byte*)t1->key, *(t1->keylen));
|
||||||
|
|
||||||
|
t->data = (datatuple::data_t) (arr+isize+isize+ *(t1->keylen));
|
||||||
|
memcpy((byte*)t->data, (byte*)t1->data, *(t1->datalen));
|
||||||
|
memcpy(((byte*)t->data) + *(t1->datalen), (byte*)t2->data, *(t2->datalen));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* replaces the data with data from t2
|
||||||
|
*
|
||||||
|
* deletes are handled by the tuplemerger::merge function
|
||||||
|
* so here neither t1 nor t2 is a delete datatuple
|
||||||
|
**/
|
||||||
|
datatuple* replace_merger(datatuple *t1, datatuple *t2)
|
||||||
|
{
|
||||||
|
static const size_t isize = sizeof(uint32_t);
|
||||||
|
struct datatuple *t = (datatuple*) malloc(sizeof(datatuple));
|
||||||
|
|
||||||
|
byte *arr = (byte*)malloc(t2->byte_length());
|
||||||
|
|
||||||
|
t->keylen = (uint32_t*) arr;
|
||||||
|
*(t->keylen) = *(t2->keylen);
|
||||||
|
|
||||||
|
t->datalen = (uint32_t*) (arr+isize);
|
||||||
|
*(t->datalen) = *(t2->datalen);
|
||||||
|
|
||||||
|
t->key = (datatuple::key_t) (arr+isize+isize);
|
||||||
|
memcpy((byte*)t->key, (byte*)t2->key, *(t2->keylen));
|
||||||
|
|
||||||
|
t->data = (datatuple::data_t) (arr+isize+isize+ *(t2->keylen));
|
||||||
|
memcpy((byte*)t->data, (byte*)t2->data, *(t2->datalen));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
|
||||||
|
}
|
34
tuplemerger.h
Normal file
34
tuplemerger.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _TUPLE_MERGER_H_
|
||||||
|
#define _TUPLE_MERGER_H_
|
||||||
|
|
||||||
|
struct datatuple;
|
||||||
|
|
||||||
|
typedef datatuple* (*merge_fn_t) (datatuple*, datatuple *);
|
||||||
|
|
||||||
|
datatuple* append_merger(datatuple *t1, datatuple *t2);
|
||||||
|
|
||||||
|
datatuple* replace_merger(datatuple *t1, datatuple *t2);
|
||||||
|
|
||||||
|
|
||||||
|
class tuplemerger
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
tuplemerger(merge_fn_t merge_fp)
|
||||||
|
{
|
||||||
|
this->merge_fp = merge_fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
datatuple* merge(datatuple *t1, datatuple *t2);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
merge_fn_t merge_fp;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue