Commit 8f72171a authored by unknown's avatar unknown

Merge ssmith@bk-internal.mysql.com:/home/bk/mysql-5.0

into  mysql.com:/home/stewart/Documents/MySQL/5.0/main
parents 09d7123f 10ee4823
......@@ -553,7 +553,6 @@ extern const GlobalSignalNumber NO_OF_SIGNAL_NAMES;
#define GSN_STATISTICS_CONF 454
#define GSN_START_ORD 455
/* 456 unused */
/* 457 unused */
#define GSN_EVENT_SUBSCRIBE_REQ 458
......@@ -835,14 +834,6 @@ extern const GlobalSignalNumber NO_OF_SIGNAL_NAMES;
/* Start Global Replication */
#define GSN_GREP_REQ 656
/**
* Management server
*/
#define GSN_MGM_LOCK_CONFIG_REQ 657
#define GSN_MGM_LOCK_CONFIG_REP 658
#define GSN_MGM_UNLOCK_CONFIG_REQ 659
#define GSN_MGM_UNLOCK_CONFIG_REP 660
#define GSN_UTIL_CREATE_LOCK_REQ 132
#define GSN_UTIL_CREATE_LOCK_REF 133
#define GSN_UTIL_CREATE_LOCK_CONF 188
......@@ -900,6 +891,7 @@ extern const GlobalSignalNumber NO_OF_SIGNAL_NAMES;
#define GSN_RESUME_REQ 682
#define GSN_STOP_REQ 443
#define GSN_STOP_REF 444
#define GSN_STOP_CONF 456
#define GSN_API_VERSION_REQ 697
#define GSN_API_VERSION_CONF 698
......
......@@ -49,12 +49,11 @@ class ApiVersionConf {
*/
friend class MgmtSrv;
public:
STATIC_CONST( SignalLength = 3 );
STATIC_CONST( SignalLength = 4 );
Uint32 senderRef;
Uint32 nodeId; //api node id
Uint32 version; // Version of API node
Uint32 inet_addr;
};
#endif
......@@ -67,6 +67,13 @@ public:
static bool getStopAbort(const Uint32 & requestInfo);
};
struct StopConf
{
STATIC_CONST( SignalLength = 2 );
Uint32 senderData;
Uint32 nodeState;
};
class StopRef
{
/**
......@@ -86,7 +93,8 @@ public:
OK = 0,
NodeShutdownInProgress = 1,
SystemShutdownInProgress = 2,
NodeShutdownWouldCauseSystemCrash = 3
NodeShutdownWouldCauseSystemCrash = 3,
TransactionAbortFailed = 4
};
public:
......
......@@ -148,9 +148,9 @@ extern "C" {
/** NDB_MGM_EVENT_CATEGORY_INFO */
NDB_LE_InfoEvent = 49,
/* GREP */
NDB_LE_GrepSubscriptionInfo = 52,
NDB_LE_GrepSubscriptionAlert = 53,
/* SINGLE USER */
NDB_LE_SingleUser = 52,
/* NDB_LE_ UNUSED = 53, */
/** NDB_MGM_EVENT_CATEGORY_BACKUP */
NDB_LE_BackupStarted = 54,
......@@ -593,6 +593,11 @@ extern "C" {
unsigned backup_id;
unsigned error;
} BackupAborted;
/** Log event data @ref NDB_LE_SingleUser */
struct {
unsigned type;
unsigned node_id;
} SingleUser;
#ifndef DOXYGEN_FIX
};
#else
......
......@@ -633,6 +633,27 @@ void getTextBackupAborted(QQQQ) {
theData[3]);
}
void getTextSingleUser(QQQQ) {
switch (theData[1])
{
case 0:
BaseString::snprintf(m_text, m_text_len, "Entering single user mode");
break;
case 1:
BaseString::snprintf(m_text, m_text_len,
"Entered single user mode "
"Node %d has exclusive access", theData[2]);
break;
case 2:
BaseString::snprintf(m_text, m_text_len,"Exiting single user mode");
break;
default:
BaseString::snprintf(m_text, m_text_len,
"Unknown single user report %d", theData[1]);
break;
}
}
#if 0
BaseString::snprintf(m_text,
m_text_len,
......@@ -716,6 +737,9 @@ const EventLoggerBase::EventRepLogLevelMatrix EventLoggerBase::matrix[] = {
ROW(CreateLogBytes, LogLevel::llInfo, 11, Logger::LL_INFO ),
ROW(InfoEvent, LogLevel::llInfo, 2, Logger::LL_INFO ),
//Single User
ROW(SingleUser, LogLevel::llInfo, 7, Logger::LL_INFO ),
// Backup
ROW(BackupStarted, LogLevel::llBackup, 7, Logger::LL_INFO ),
ROW(BackupCompleted, LogLevel::llBackup, 7, Logger::LL_INFO ),
......
......@@ -1962,6 +1962,11 @@ Ndbcntr::execRESUME_REQ(Signal* signal){
//ResumeRef * const ref = (ResumeRef *)&signal->theData[0];
jamEntry();
signal->theData[0] = NDB_LE_SingleUser;
signal->theData[1] = 2;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
//Uint32 senderData = req->senderData;
//BlockReference senderRef = req->senderRef;
NodeState newState(NodeState::SL_STARTED);
......@@ -2000,12 +2005,11 @@ Ndbcntr::execSTOP_REQ(Signal* signal){
return;
}
if(c_stopRec.stopReq.senderRef != 0 && !singleuser){
jam();
if(c_stopRec.stopReq.senderRef != 0){
/**
* Requested a system shutdown
*/
if(StopReq::getSystemStop(req->requestInfo)){
if(!singleuser && StopReq::getSystemStop(req->requestInfo)){
jam();
sendSignalWithDelay(reference(), GSN_STOP_REQ, signal, 100,
StopReq::SignalLength);
......@@ -2027,23 +2031,28 @@ Ndbcntr::execSTOP_REQ(Signal* signal){
c_stopRec.stopReq = * req;
c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
if(StopReq::getSystemStop(c_stopRec.stopReq.requestInfo) && !singleuser) {
jam();
if(StopReq::getPerformRestart(c_stopRec.stopReq.requestInfo)){
((Configuration&)theConfiguration).stopOnError(false);
}
}
if(!singleuser) {
if(StopReq::getSystemStop(c_stopRec.stopReq.requestInfo)) {
jam();
if(StopReq::getPerformRestart(c_stopRec.stopReq.requestInfo)){
((Configuration&)theConfiguration).stopOnError(false);
}
}
if(!c_stopRec.checkNodeFail(signal)){
jam();
return;
}
signal->theData[0] = NDB_LE_NDBStopStarted;
signal->theData[1] = StopReq::getSystemStop(c_stopRec.stopReq.requestInfo) ? 1 : 0;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
}
signal->theData[0] = NDB_LE_NDBStopStarted;
signal->theData[1] = StopReq::getSystemStop(c_stopRec.stopReq.requestInfo) ? 1 : 0;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
else
{
signal->theData[0] = NDB_LE_SingleUser;
signal->theData[1] = 0;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
}
NodeState newState(NodeState::SL_STOPPING_1,
StopReq::getSystemStop(c_stopRec.stopReq.requestInfo));
......@@ -2125,9 +2134,11 @@ Ndbcntr::StopRecord::checkNodeFail(Signal* signal){
stopReq.senderRef = 0;
NodeState newState(NodeState::SL_STARTED);
cntr.updateNodeState(signal, newState);
if (cntr.getNodeState().startLevel != NodeState::SL_SINGLEUSER)
{
NodeState newState(NodeState::SL_STARTED);
cntr.updateNodeState(signal, newState);
}
signal->theData[0] = NDB_LE_NDBStopAborted;
cntr.sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
......@@ -2223,12 +2234,24 @@ void Ndbcntr::execABORT_ALL_CONF(Signal* signal){
jamEntry();
if(c_stopRec.stopReq.singleuser) {
jam();
NodeState newState(NodeState::SL_SINGLEUSER);
newState.setSingleUser(true);
newState.setSingleUserApi(c_stopRec.stopReq.singleUserApi);
updateNodeState(signal, newState);
c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
StopConf * const stopConf = (StopConf *)&signal->theData[0];
stopConf->senderData = c_stopRec.stopReq.senderData;
stopConf->nodeState = (Uint32) NodeState::SL_SINGLEUSER;
sendSignal(c_stopRec.stopReq.senderRef, GSN_STOP_CONF, signal, StopConf::SignalLength, JBB);
c_stopRec.stopReq.senderRef = 0; // the command is done
signal->theData[0] = NDB_LE_SingleUser;
signal->theData[1] = 1;
signal->theData[2] = c_stopRec.stopReq.singleUserApi;
sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
}
else
{
......@@ -2246,7 +2269,13 @@ void Ndbcntr::execABORT_ALL_CONF(Signal* signal){
void Ndbcntr::execABORT_ALL_REF(Signal* signal){
jamEntry();
ndbrequire(false);
AbortAllRef *abortAllRef = (AbortAllRef *)&signal->theData[0];
AbortAllRef::ErrorCode errorCode = (AbortAllRef::ErrorCode) abortAllRef->errorCode;
StopRef * const stopRef = (StopRef *)&signal->theData[0];
stopRef->senderData = c_stopRec.stopReq.senderData;
stopRef->errorCode = StopRef::TransactionAbortFailed;
sendSignal(c_stopRec.stopReq.senderRef, GSN_STOP_REF, signal, StopRef::SignalLength, JBB);
}
void
......
......@@ -2013,6 +2013,8 @@ Qmgr::execAPI_VERSION_REQ(Signal * signal) {
else
conf->version = 0;
conf->nodeId = nodeId;
struct in_addr in= globalTransporterRegistry.get_connect_address(nodeId);
conf->inet_addr= in.s_addr;
sendSignal(senderRef,
GSN_API_VERSION_CONF,
......
......@@ -289,6 +289,8 @@ struct Ndb_logevent_body_row ndb_logevent_body[]= {
ROW( BackupAborted, "backup_id", 2, backup_id),
ROW( BackupAborted, "error", 3, error),
ROW( SingleUser, "type", 1, type),
ROW( SingleUser, "node_id", 2, node_id),
{ NDB_LE_ILLEGAL_TYPE, 0, 0, 0, 0, 0}
};
......
......@@ -1443,9 +1443,8 @@ CommandInterpreter::executeEnterSingleUser(char* parameters)
ndbout_c("Entering single user mode for node %d failed", nodeId);
printError();
} else {
ndbout_c("Entering single user mode");
ndbout_c("Access will be granted for API node %d only.", nodeId);
ndbout_c("Use ALL STATUS to see when single user mode has been entered.");
ndbout_c("Single user mode entered");
ndbout_c("Access is granted for API node %d only.", nodeId);
}
}
......@@ -1458,7 +1457,7 @@ CommandInterpreter::executeExitSingleUser(char* parameters)
printError();
} else {
ndbout_c("Exiting single user mode in progress.");
ndbout_c("Use ALL STATUS to see when single user mode has been exited.");
ndbout_c("Use ALL STATUS or SHOW to see when single user mode has been exited.");
}
}
......
......@@ -179,90 +179,3 @@ void Config::printConfigFile(NdbOut &out) const {
}
#endif
}
Uint32
Config::getGenerationNumber() const {
#if 0
Uint32 ret;
const Properties *prop = NULL;
get("SYSTEM", &prop);
if(prop != NULL)
if(prop->get("ConfigGenerationNumber", &ret))
return ret;
return 0;
#else
return 0;
#endif
}
int
Config::setGenerationNumber(Uint32 gen) {
#if 0
Properties *prop = NULL;
getCopy("SYSTEM", &prop);
if(prop != NULL) {
MGM_REQUIRE(prop->put("ConfigGenerationNumber", gen, true));
MGM_REQUIRE(put("SYSTEM", prop, true));
return 0;
}
return -1;
#else
return -1;
#endif
}
bool
Config::change(const BaseString &section,
const BaseString &param,
const BaseString &value) {
#if 0
const char *name;
Properties::Iterator it(this);
for(name = it.first(); name != NULL; name = it.next()) {
Properties *prop = NULL;
if(strcasecmp(section.c_str(), name) == 0) {
getCopy(name, &prop);
if(prop == NULL) /* doesn't exist */
return false;
if(value == "") {
prop->remove(param.c_str());
put(section.c_str(), prop, true);
} else {
PropertiesType t;
if(!prop->getTypeOf(param.c_str(), &t)) /* doesn't exist */
return false;
switch(t) {
case PropertiesType_Uint32:
long val;
char *ep;
errno = 0;
val = strtol(value.c_str(), &ep, 0);
if(value.length() == 0 || *ep != '\0') /* not a number */
return false;
if(errno == ERANGE)
return false;
prop->put(param.c_str(), (unsigned int)val, true);
put(section.c_str(), prop, true);
break;
case PropertiesType_char:
prop->put(param.c_str(), value.c_str(), true);
put(section.c_str(), prop, true);
break;
default:
return false;
}
}
break;
}
}
return true;
#else
return false;
#endif
}
......@@ -60,16 +60,6 @@ public:
printConfigFile(ndb);
}
Uint32 getGenerationNumber() const;
int setGenerationNumber(Uint32);
/** Change configuration
*/
bool change(const BaseString &section,
const BaseString &param,
const BaseString &value);
/**
* Info
*/
......
This diff is collapsed.
......@@ -22,15 +22,17 @@
#include <NdbCondition.h>
#include <mgmapi.h>
#include <NdbTCP.h>
#include <ConfigRetriever.hpp>
#include <Vector.hpp>
#include <NodeBitmask.hpp>
#include <signaldata/ManagementServer.hpp>
#include "SignalQueue.hpp"
#include <ndb_version.h>
#include <EventLogger.hpp>
#include <signaldata/EventSubscribeReq.hpp>
#include <SignalSender.hpp>
/**
* @desc Block number for Management server.
* @todo This should probably be somewhere else. I don't know where atm.
......@@ -200,51 +202,26 @@ public:
~MgmtSrvr();
int status(int processId,
ndb_mgm_node_status * status,
/**
* Get status on a node.
* address may point to a common area (e.g. from inet_addr)
* There is no gaurentee that it is preserved across calls.
* Copy the string if you are not going to use it immediately.
*/
int status(int nodeId,
ndb_mgm_node_status * status,
Uint32 * version,
Uint32 * phase,
bool * systemShutdown,
Uint32 * dynamicId,
Uint32 * nodeGroup,
Uint32 * connectCount);
Uint32 * connectCount,
const char **address);
// All the functions below may return any of this error codes:
// NO_CONTACT_WITH_PROCESS, PROCESS_NOT_CONFIGURED, WRONG_PROCESS_TYPE,
// COULD_NOT_ALLOCATE_MEMORY, SEND_OR_RECEIVE_FAILED
typedef void (* StopCallback)(int nodeId, void * anyData, int errorCode);
typedef void (* VersionCallback)(int nodeId, int version,
void * anyData, int errorCode);
typedef void (* EnterSingleCallback)(int nodeId, void * anyData,
int errorCode);
typedef void (* ExitSingleCallback)(int nodeId, void * anyData,
int errorCode);
/**
* Lock configuration
*/
int lockConf();
/**
* Unlock configuration, and commit it if commit is true
*/
int unlockConf(bool commit);
/**
* Commit new configuration
*/
int commitConfig();
/**
* Rollback configuration
*/
int rollbackConfig();
/**
* Save a configuration to permanent storage
*/
......@@ -273,12 +250,12 @@ public:
* @param processId: Id of the DB process to stop
* @return 0 if succeeded, otherwise: as stated above, plus:
*/
int stopNode(int nodeId, bool abort = false, StopCallback = 0, void *any= 0);
int stopNode(int nodeId, bool abort = false);
/**
* Stop the system
*/
int stop(int * cnt = 0, bool abort = false, StopCallback = 0, void *any = 0);
int stop(int * cnt = 0, bool abort = false);
/**
* print version info about a node
......@@ -286,27 +263,18 @@ public:
* @param processId: Id of the DB process to stop
* @return 0 if succeeded, otherwise: as stated above, plus:
*/
int versionNode(int nodeId, bool abort = false,
VersionCallback = 0, void *any= 0);
int versionNode(int nodeId, Uint32 &version, const char **address);
/**
* print version info about all node in the system
*/
int version(int * cnt = 0, bool abort = false,
VersionCallback = 0, void *any = 0);
/**
* Maintenance on the system
*/
int enterSingleUser(int * cnt = 0, Uint32 singleuserNodeId = 0,
EnterSingleCallback = 0, void *any = 0);
int enterSingleUser(int * cnt = 0, Uint32 singleuserNodeId = 0);
/**
* Resume from maintenance on the system
*/
int exitSingleUser(int * cnt = 0, bool abort = false,
ExitSingleCallback = 0, void *any = 0);
int exitSingleUser(int * cnt = 0, bool abort = false);
/**
* Start DB process.
......@@ -320,15 +288,14 @@ public:
* @param processId: Id of the DB process to start
*/
int restartNode(int processId, bool nostart, bool initialStart,
bool abort = false,
StopCallback = 0, void * anyData = 0);
bool abort = false);
/**
* Restart the system
*/
int restart(bool nostart, bool initialStart,
bool abort = false,
int * stopCount = 0, StopCallback = 0, void * anyData = 0);
int * stopCount = 0);
struct BackupEvent {
enum Event {
......@@ -482,13 +449,6 @@ public:
*/
const Config * getConfig() const;
/**
* Change configuration paramter
*/
bool changeConfig(const BaseString &section,
const BaseString &param,
const BaseString &value);
/**
* Returns the node count for the specified node type.
*
......@@ -497,11 +457,6 @@ public:
*/
int getNodeCount(enum ndb_mgm_node_type type) const;
/**
* Returns the nodeId of the management master
*/
NodeId getPrimaryNode() const;
/**
* Returns the port number.
* @return port number.
......@@ -528,8 +483,17 @@ public:
private:
//**************************************************************************
int setEventReportingLevel(int processId, LogLevel::EventCategory, Uint32);
int send(SignalSender &ss, SimpleSignal &ssig, Uint32 node, Uint32 node_type);
int sendSTOP_REQ(NodeId nodeId,
NodeBitmask &stoppedNodes,
Uint32 singleUserNodeId,
bool abort,
bool stop,
bool restart,
bool nostart,
bool initialStart);
/**
* Check if it is possible to send a signal to a (DB) process
*
......@@ -593,9 +557,6 @@ private:
// Returns: -
//**************************************************************************
void handle_MGM_LOCK_CONFIG_REQ(NdbApiSignal *signal);
void handle_MGM_UNLOCK_CONFIG_REQ(NdbApiSignal *signal);
//**************************************************************************
// Specific signal handling data
//**************************************************************************
......@@ -619,59 +580,8 @@ private:
enum WaitSignalType {
NO_WAIT, // We don't expect to receive any signal
WAIT_SET_VAR, // Accept SET_VAR_CONF and SET_VAR_REF
WAIT_SUBSCRIBE_CONF, // Accept event subscription confirmation
WAIT_STOP,
WAIT_BACKUP_STARTED,
WAIT_BACKUP_COMPLETED,
WAIT_VERSION,
WAIT_NODEFAILURE
WAIT_SUBSCRIBE_CONF // Accept event subscription confirmation
};
/**
* Get an unused signal
* @return A signal if succeeded, NULL otherwise
*/
NdbApiSignal* getSignal();
/**
* Add a signal to the list of unused signals
* @param signal: The signal to add
*/
void releaseSignal(NdbApiSignal* signal);
/**
* Remove a signal from the list of unused signals and delete
* the memory for it.
*/
void freeSignal();
/**
* Send a signal
* @param processId: Id of the receiver process
* @param waitState: State denoting a set of signals we accept to receive
* @param signal: The signal to send
* @return 0 if succeeded, -1 otherwise
*/
int sendSignal(Uint16 processId, WaitSignalType waitState,
NdbApiSignal* signal, bool force = false);
/**
* Send a signal and wait for an answer signal
* @param processId: Id of the receiver process
* @param waitState: State denoting a set of signals we accept to receive.
* @param signal: The signal to send
* @return 0 if succeeded, -1 otherwise (for example failed to send or
* failed to receive expected signal).
*/
int sendRecSignal(Uint16 processId, WaitSignalType waitState,
NdbApiSignal* signal, bool force = false,
int waitTime = WAIT_FOR_RESPONSE_TIMEOUT);
/**
* Wait for a signal to arrive.
* @return 0 if signal arrived, -1 otherwise
*/
int receiveOptimisedResponse(int waitTime);
/**
* This function is called from "outside" of MgmtSrvr
......@@ -682,7 +592,7 @@ private:
static void signalReceivedNotification(void* mgmtSrvr,
NdbApiSignal* signal,
struct LinearSectionPtr ptr[3]);
/**
* Called from "outside" of MgmtSrvr when a DB process has died.
* @param mgmtSrvr: The MgmtSrvr object wreceiveOptimisedResponsehich
......@@ -719,31 +629,7 @@ private:
class TransporterFacade * theFacade;
class SignalQueue m_signalRecvQueue;
struct StopRecord {
StopRecord(){ inUse = false; callback = 0; singleUserMode = false;}
bool inUse;
bool singleUserMode;
int sentCount;
int reply;
int nodeId;
void * anyData;
StopCallback callback;
};
StopRecord m_stopRec;
struct VersionRecord {
VersionRecord(){ inUse = false; callback = 0;}
bool inUse;
Uint32 version[MAX_NODES];
VersionCallback callback;
};
VersionRecord m_versionRec;
int sendVersionReq( int processId);
void handleStopReply(NodeId nodeId, Uint32 errCode);
int sendVersionReq( int processId, Uint32 &version, const char **address);
int translateStopRef(Uint32 errCode);
bool _isStopThread;
......@@ -764,14 +650,8 @@ private:
static void *logLevelThread_C(void *);
void logLevelThreadRun();
struct NdbThread *m_signalRecvThread;
static void *signalRecvThread_C(void *);
void signalRecvThreadRun();
Config *_props;
int send(class NdbApiSignal* signal, Uint32 node, Uint32 node_type);
ConfigRetriever *m_config_retriever;
};
......
......@@ -23,228 +23,6 @@
#include <ConfigRetriever.hpp>
#include <ndb_version.h>
void
MgmtSrvr::handle_MGM_LOCK_CONFIG_REQ(NdbApiSignal *signal) {
NodeId sender = refToNode(signal->theSendersBlockRef);
const MgmLockConfigReq * const req = CAST_CONSTPTR(MgmLockConfigReq, signal->getDataPtr());
NdbApiSignal *reply = getSignal();
if(signal == NULL)
return; /** @todo handle allocation failure */
reply->set(TestOrd::TraceAPI,
MGMSRV,
GSN_MGM_LOCK_CONFIG_REP,
MgmLockConfigRep::SignalLength);
MgmLockConfigRep *lockRep = CAST_PTR(MgmLockConfigRep, reply->getDataPtrSend());
lockRep->errorCode = MgmLockConfigRep::UNKNOWN_ERROR;
if(req->newConfigGeneration < m_nextConfigGenerationNumber) {
lockRep->errorCode = MgmLockConfigRep::GENERATION_MISMATCH;
goto done;
}
NdbMutex_Lock(m_configMutex);
m_nextConfigGenerationNumber = req->newConfigGeneration+1;
lockRep->errorCode = MgmLockConfigRep::OK;
done:
sendSignal(sender, NO_WAIT, reply, true);
NdbMutex_Unlock(m_configMutex);
return;
}
void
MgmtSrvr::handle_MGM_UNLOCK_CONFIG_REQ(NdbApiSignal *signal) {
NodeId sender = refToNode(signal->theSendersBlockRef);
const MgmUnlockConfigReq * const req = CAST_CONSTPTR(MgmUnlockConfigReq, signal->getDataPtr());
MgmUnlockConfigRep *unlockRep;
NdbApiSignal *reply = getSignal();
if(signal == NULL)
goto error; /** @todo handle allocation failure */
reply->set(TestOrd::TraceAPI,
MGMSRV,
GSN_MGM_UNLOCK_CONFIG_REP,
MgmUnlockConfigRep::SignalLength);
unlockRep = CAST_PTR(MgmUnlockConfigRep, reply->getDataPtrSend());
unlockRep->errorCode = MgmUnlockConfigRep::UNKNOWN_ERROR;
NdbMutex_Lock(m_configMutex);
if(req->commitConfig == 1) {
m_newConfig = fetchConfig();
commitConfig();
} else
rollbackConfig();
unlockRep->errorCode = MgmUnlockConfigRep::OK;
sendSignal(sender, NO_WAIT, reply, true);
error:
NdbMutex_Unlock(m_configMutex);
return;
}
/**
* Prepare all MGM nodes for configuration changes
*
* @returns 0 on success, or -1 on failure
*/
int
MgmtSrvr::lockConf() {
int result = -1;
MgmLockConfigReq* lockReq;
NodeId node = 0;
/* Check if this is the master node */
if(getPrimaryNode() != _ownNodeId)
goto done;
if(NdbMutex_Trylock(m_configMutex) != 0)
return -1;
m_newConfig = new Config(*_config); /* copy the existing config */
_config = m_newConfig;
m_newConfig = new Config(*_config);
m_nextConfigGenerationNumber++;
/* Make sure the new configuration _always_ is at least one step older */
if(m_nextConfigGenerationNumber < m_newConfig->getGenerationNumber()+1)
m_nextConfigGenerationNumber = _config->getGenerationNumber()+1;
m_newConfig->setGenerationNumber(m_nextConfigGenerationNumber);
node = 0;
while(getNextNodeId(&node, NDB_MGM_NODE_TYPE_MGM)) {
if(node != _ownNodeId) {
NdbApiSignal* signal = getSignal();
if (signal == NULL) {
result = COULD_NOT_ALLOCATE_MEMORY;
goto done;
}
lockReq = CAST_PTR(MgmLockConfigReq, signal->getDataPtrSend());
signal->set(TestOrd::TraceAPI,
MGMSRV,
GSN_MGM_LOCK_CONFIG_REQ,
MgmLockConfigReq::SignalLength);
lockReq->newConfigGeneration = m_nextConfigGenerationNumber;
result = sendSignal(node, NO_WAIT, signal, true);
NdbApiSignal *reply =
m_signalRecvQueue.waitFor(GSN_MGM_LOCK_CONFIG_REP, 0);
if(reply == NULL) {
/** @todo handle timeout/error */
ndbout << __FILE__ << ":" << __LINE__ << endl;
result = -1;
goto done;
}
}
}
done:
NdbMutex_Unlock(m_configMutex);
return result;
}
/**
* Unlocks configuration
*
* @returns 0 on success, ! 0 on error
*/
int
MgmtSrvr::unlockConf(bool commit) {
int result = -1;
MgmUnlockConfigReq* unlockReq;
NodeId node = 0;
/* Check if this is the master node */
if(getPrimaryNode() != _ownNodeId)
goto done;
errno = 0;
if(NdbMutex_Lock(m_configMutex) != 0)
return -1;
if(commit)
commitConfig();
else
rollbackConfig();
node = 0;
while(getNextNodeId(&node, NDB_MGM_NODE_TYPE_MGM)) {
if(node != _ownNodeId) {
NdbApiSignal* signal = getSignal();
if (signal == NULL) {
result = COULD_NOT_ALLOCATE_MEMORY;
goto done;
}
unlockReq = CAST_PTR(MgmUnlockConfigReq, signal->getDataPtrSend());
signal->set(TestOrd::TraceAPI,
MGMSRV,
GSN_MGM_UNLOCK_CONFIG_REQ,
MgmUnlockConfigReq::SignalLength);
unlockReq->commitConfig = commit;
result = sendSignal(node, NO_WAIT, signal, true);
NdbApiSignal *reply =
m_signalRecvQueue.waitFor(GSN_MGM_UNLOCK_CONFIG_REP, 0);
if(reply == NULL) {
/** @todo handle timeout/error */
result = -1;
goto done;
}
}
}
done:
NdbMutex_Unlock(m_configMutex);
return result;
}
/**
* Commit the new configuration
*/
int
MgmtSrvr::commitConfig() {
int ret = saveConfig(m_newConfig);
delete _config;
_config = m_newConfig;
m_newConfig = NULL;
ndbout << "commit " << ret << endl;
return ret;
}
/**
* Rollback to the old configuration
*/
int
MgmtSrvr::rollbackConfig() {
delete m_newConfig;
m_newConfig = NULL;
ndbout << "rollback" << endl;
return saveConfig(_config);
}
/**
* Save a configuration to the running configuration file
*/
......@@ -288,12 +66,3 @@ MgmtSrvr::fetchConfig() {
}
return 0;
}
bool
MgmtSrvr::changeConfig(const BaseString &section,
const BaseString &param,
const BaseString &value) {
if(m_newConfig == NULL)
return false;
return m_newConfig->change(section, param, value);
}
......@@ -20,123 +20,3 @@
// Some kind of reuse should be preferred.
//******************************************************************************
#include "MgmtSrvr.hpp"
#include <NdbApiSignal.hpp>
#include <NdbTick.h>
NdbApiSignal*
MgmtSrvr::getSignal()
{
NdbApiSignal* tSignal;
tSignal = theSignalIdleList;
if (tSignal != NULL){
NdbApiSignal* tSignalNext = tSignal->next();
tSignal->next(NULL);
theSignalIdleList = tSignalNext;
return tSignal;
} else
{
tSignal = new NdbApiSignal(_ownReference);
if (tSignal != NULL)
tSignal->next(NULL);
}
return tSignal;
}
void
MgmtSrvr::releaseSignal(NdbApiSignal* aSignal)
{
aSignal->next(theSignalIdleList);
theSignalIdleList = aSignal;
}
void
MgmtSrvr::freeSignal()
{
NdbApiSignal* tSignal = theSignalIdleList;
theSignalIdleList = tSignal->next();
delete tSignal;
}
int
MgmtSrvr::sendSignal(Uint16 aNodeId,
WaitSignalType aWaitState,
NdbApiSignal* aSignal,
bool force)
{
int tReturnCode;
theFacade->lock_mutex();
if(force){
tReturnCode = theFacade->sendSignalUnCond(aSignal,
aNodeId);
} else {
tReturnCode = theFacade->sendSignal(aSignal,
aNodeId);
}
releaseSignal(aSignal);
if (tReturnCode == -1) {
theFacade->unlock_mutex();
return -1;
}
theWaitState = aWaitState;
theFacade->unlock_mutex();
return 0;
}
int
MgmtSrvr::sendRecSignal(Uint16 aNodeId,
WaitSignalType aWaitState,
NdbApiSignal* aSignal,
bool force,
int waitTime)
{
int tReturnCode;
theFacade->lock_mutex();
if(force){
tReturnCode = theFacade->sendSignalUnCond(aSignal, aNodeId);
} else {
tReturnCode = theFacade->sendSignalUnCond(aSignal, aNodeId);
}
releaseSignal(aSignal);
if (tReturnCode == -1) {
theFacade->unlock_mutex();
return -1;
}
theWaitState = aWaitState;
theWaitNode = aNodeId;
return receiveOptimisedResponse(waitTime);
}
int
MgmtSrvr::receiveOptimisedResponse(int waitTime)
{
int tResultCode;
theFacade->checkForceSend(_blockNumber);
NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime;
while (theWaitState != NO_WAIT && theWaitState != WAIT_NODEFAILURE
&& waitTime > 0) {
NdbCondition_WaitTimeout(theMgmtWaitForResponseCondPtr,
theFacade->theMutexPtr,
waitTime);
if(theWaitState == NO_WAIT || theWaitState == WAIT_NODEFAILURE)
break;
waitTime = (maxTime - NdbTick_CurrentMillisecond());
}//while
if(theWaitState == NO_WAIT) {
tResultCode = 0;
} else {
tResultCode = -1;
}
theFacade->unlock_mutex();
return tResultCode;
}
......@@ -222,21 +222,6 @@ ParserRow<MgmApiSession> commands[] = {
MGM_ARG("level", Int, Mandatory, "Severety level"),
MGM_ARG("enable", Int, Mandatory, "1=disable, 0=enable, -1=toggle"),
MGM_CMD("config lock", &MgmApiSession::configLock, ""),
MGM_CMD("config unlock", &MgmApiSession::configUnlock, ""),
MGM_ARG("commit", Int, Mandatory, "Commit changes"),
MGM_CMD("config change", &MgmApiSession::configChange, ""),
MGM_ARG("section", String, Mandatory, "Section"),
MGM_ARG("parameter", String, Mandatory, "Parameter"),
MGM_ARG("value", String, Mandatory, "Value"),
MGM_CMD("config lock", &MgmApiSession::configLock, ""),
MGM_CMD("config unlock", &MgmApiSession::configUnlock, ""),
MGM_ARG("commit", Int, Mandatory, "Commit changes"),
MGM_CMD("set parameter", &MgmApiSession::setParameter, ""),
MGM_ARG("node", String, Mandatory, "Node"),
MGM_ARG("parameter", String, Mandatory, "Parameter"),
......@@ -940,8 +925,10 @@ printNodeStatus(OutputStream *output,
nodeGroup = 0,
connectCount = 0;
bool system;
mgmsrv.status(nodeId, &status, &version, &startPhase,
&system, &dynamicId, &nodeGroup, &connectCount);
const char *address= NULL;
mgmsrv.status(nodeId, &status, &version, &startPhase,
&system, &dynamicId, &nodeGroup, &connectCount,
&address);
output->println("node.%d.type: %s",
nodeId,
ndb_mgm_get_node_type_string(type));
......@@ -953,7 +940,7 @@ printNodeStatus(OutputStream *output,
output->println("node.%d.dynamic_id: %d", nodeId, dynamicId);
output->println("node.%d.node_group: %d", nodeId, nodeGroup);
output->println("node.%d.connect_count: %d", nodeId, connectCount);
output->println("node.%d.address: %s", nodeId, mgmsrv.get_connect_address(nodeId));
output->println("node.%d.address: %s", nodeId, address);
}
}
......@@ -1222,42 +1209,6 @@ MgmApiSession::setLogFilter(Parser_t::Context &ctx,
m_output->println("");
}
void
MgmApiSession::configLock(Parser_t::Context &,
Properties const &) {
int ret = m_mgmsrv.lockConf();
m_output->println("config lock reply");
m_output->println("result: %d", ret);
m_output->println("");
}
void
MgmApiSession::configUnlock(Parser_t::Context &,
Properties const &args) {
Uint32 commit;
args.get("commit", &commit);
int ret = m_mgmsrv.unlockConf(commit == 1);
m_output->println("config unlock reply");
m_output->println("result: %d", ret);
m_output->println("");
}
void
MgmApiSession::configChange(Parser_t::Context &,
Properties const &args) {
BaseString section, param, value;
args.get("section", section);
args.get("parameter", param);
args.get("value", value);
int ret = m_mgmsrv.changeConfig(section.c_str(),
param.c_str(),
value.c_str());
m_output->println("config change reply");
m_output->println("result: %d", ret);
m_output->println("");
}
static NdbOut&
operator<<(NdbOut& out, const LogLevel & ll)
{
......
......@@ -83,9 +83,6 @@ public:
void setClusterLogLevel(Parser_t::Context &ctx,
const class Properties &args);
void setLogFilter(Parser_t::Context &ctx, const class Properties &args);
void configLock(Parser_t::Context &ctx, const class Properties &args);
void configUnlock(Parser_t::Context &ctx, const class Properties &args);
void configChange(Parser_t::Context &ctx, const class Properties &args);
void setParameter(Parser_t::Context &ctx, const class Properties &args);
void setConnectionParameter(Parser_t::Context &ctx,
......
#!/usr/bin/perl -w
use strict;
use DBI;
use POSIX;
use HTML::Template;
# MySQL Cluster size estimator
# ----------------------------
#
# (C)2005 MySQL AB
#
#
# The purpose of this tool is to work out storage requirements
# from an existing MySQL database.
#
# This involves connecting to a mysql server and throwing a bunch
# of queries at it.
#
# We currently estimate sizes for: 4.1, 5.0 and 5.1 to various amounts
# of accurracy.
#
# There is no warranty.
#
# BUGS
# ----
# - enum/set is 0 byte storage! Woah - efficient!
# - some float stores come out weird (when there's a comma e.g. 'float(4,1)')
# - no disk data values
# - computes the storage requirements of views (and probably MERGE)
# - ignores character sets.
my $template = HTML::Template->new(filename => 'ndb_size.tmpl',
die_on_bad_params => 0);
my $dbh;
{
my $database= $ARGV[0];
my $hostname= $ARGV[1];
my $port= $ARGV[2];
my $user= $ARGV[3];
my $password= $ARGV[4];
my $dsn = "DBI:mysql:database=$database;host=$hostname;port=$port";
$dbh= DBI->connect($dsn, $user, $password);
$template->param(db => $database);
$template->param(dsn => $dsn);
}
my @releases = ({rel=>'4.1'},{rel=>'5.0'},{rel=>'5.1'});
$template->param(releases => \@releases);
my $tables = $dbh->selectall_arrayref("show tables");
my @table_size;
sub align {
my($to,@unaligned) = @_;
my @aligned;
foreach my $x (@unaligned) {
push @aligned, $to * POSIX::floor(($x+$to-1)/$to);
}
return @aligned;
}
foreach(@{$tables})
{
my $table= @{$_}[0];
my @columns;
my $info= $dbh->selectall_hashref("describe ".$dbh->quote($table),"Field");
my @count = $dbh->selectrow_array("select count(*) from "
.$dbh->quote($table));
my %columnsize; # used for index calculations
# We now work out the DataMemory usage
# sizes for 4.1, 5.0, 5.1
my @totalsize= (0,0,0);
foreach(keys %$info)
{
my @realsize = (0,0,0);
my $type;
my $size;
my $name= $_;
if($$info{$_}{Type} =~ /^(.*?)\((\d+)\)/)
{
$type= $1;
$size= $2;
}
else
{
$type= $$info{$_}{Type};
}
if($type =~ /tinyint/)
{@realsize=(1,1,1)}
elsif($type =~ /smallint/)
{@realsize=(2,2,2)}
elsif($type =~ /mediumint/)
{@realsize=(3,3,3)}
elsif($type =~ /bigint/)
{@realsize=(8,8,8)}
elsif($type =~ /int/)
{@realsize=(4,4,4)}
elsif($type =~ /float/)
{
if($size<=24)
{@realsize=(4,4,4)}
else
{@realsize=(8,8,8)}
}
elsif($type =~ /double/ || $type =~ /real/)
{@realsize=(8,8,8)}
elsif($type =~ /bit/)
{
my $a=($size+7)/8;
@realsize = ($a,$a,$a);
}
elsif($type =~ /datetime/)
{@realsize=(8,8,8)}
elsif($type =~ /timestamp/)
{@realsize=(4,4,4)}
elsif($type =~ /date/ || $type =~ /time/)
{@realsize=(3,3,3)}
elsif($type =~ /year/)
{@realsize=(1,1,1)}
elsif($type =~ /varchar/ || $type =~ /varbinary/)
{
my $fixed= 1+$size;
my @dynamic=$dbh->selectrow_array("select avg(length("
.$dbh->quote($name)
.")) from ".$dbh->quote($table));
$dynamic[0]=0 if !$dynamic[0];
@realsize= ($fixed,$fixed,ceil($dynamic[0]));
}
elsif($type =~ /binary/ || $type =~ /char/)
{@realsize=($size,$size,$size)}
elsif($type =~ /text/ || $type =~ /blob/)
{@realsize=(256,256,1)} # FIXME check if 5.1 is correct
@realsize= align(4,@realsize);
$totalsize[$_]+=$realsize[$_] foreach 0..$#totalsize;
my @realout;
push @realout,{val=>$_} foreach @realsize;
push @columns, {
name=>$name,
type=>$type,
size=>$size,
key=>$$info{$_}{Key},
datamemory=>\@realout,
};
$columnsize{$name}= \@realsize; # used for index calculations
}
# And now... the IndexMemory usage.
#
# Firstly, we assemble some information about the indexes.
# We use SHOW INDEX instead of using INFORMATION_SCHEMA so
# we can still connect to pre-5.0 mysqlds.
my %indexes;
{
my $sth= $dbh->prepare("show index from "$dbh->quote($table));
$sth->execute;
while(my $i = $sth->fetchrow_hashref)
{
$indexes{${%$i}{Key_name}}= {
type=>${%$i}{Index_type},
unique=>!${%$i}{Non_unique},
comment=>${%$i}{Comment},
} if !defined($indexes{${%$i}{Key_name}});
$indexes{${%$i}{Key_name}}{columns}[${%$i}{Seq_in_index}-1]=
${%$i}{Column_name};
}
}
if(!defined($indexes{PRIMARY})) {
$indexes{PRIMARY}= {
type=>'BTREE',
unique=>1,
comment=>'Hidden pkey created by NDB',
columns=>['HIDDEN_NDB_PKEY'],
};
push @columns, {
name=>'HIDDEN_NDB_PKEY',
type=>'bigint',
size=>8,
key=>'PRI',
datamemory=>[{val=>8},{val=>8},{val=>8}],
};
$columnsize{'HIDDEN_NDB_PKEY'}= [8,8,8];
}
my @IndexDataMemory= ({val=>0},{val=>0},{val=>0});
my @RowIndexMemory= ({val=>0},{val=>0},{val=>0});
my @indexes;
foreach my $index (keys %indexes) {
my $im41= 25;
$im41+=$columnsize{$_}[0] foreach @{$indexes{$index}{columns}};
my @im = ({val=>$im41},{val=>25},{val=>25});
my @dm = ({val=>10},{val=>10},{val=>10});
push @indexes, {
name=>$index,
type=>$indexes{$index}{type},
columns=>join(',',@{$indexes{$index}{columns}}),
indexmemory=>\@im,
datamemory=>\@dm,
};
$IndexDataMemory[$_]{val}+=$dm[$_]{val} foreach 0..2;
$RowIndexMemory[$_]{val}+=$im[$_]{val} foreach 0..2;
}
# total size + 16 bytes overhead
my @TotalDataMemory;
$TotalDataMemory[$_]{val}=$IndexDataMemory[$_]{val}+$totalsize[$_]+16 foreach 0..2;
my @RowDataMemory;
push @RowDataMemory,{val=>$_} foreach @totalsize;
my @RowPerPage;
push @RowPerPage,{val=>(floor((32768-128)/$TotalDataMemory[$_]{val}))} foreach 0..$#TotalDataMemory;
my @RowPerIndexPage;
push @RowPerIndexPage,{val=>(floor(8192/$RowIndexMemory[$_]{val}))} foreach 0..$#TotalDataMemory;
my @DataMemory;
push @DataMemory,{val=>ceil(($count[0]/($RowPerPage[$_]{val})))*32} foreach 0..$#RowPerPage;
my @IndexMemory;
push @IndexMemory,{val=>ceil(($count[0]/($RowPerIndexPage[$_]{val})))*8} foreach 0..$#RowPerPage;
my $count= $count[0];
my @counts;
$counts[$_]{val}= $count foreach 0..$#releases;
push @table_size, {
table=>$table,
indexes=>\@indexes,
columns=>\@columns,
count=>\@counts,
RowDataMemory=>\@RowDataMemory,
releases=>\@releases,
IndexDataMemory=>\@IndexDataMemory,
TotalDataMemory=>\@TotalDataMemory,
RowPerPage=>\@RowPerPage,
DataMemory=>\@DataMemory,
RowIndexMemory=>\@RowIndexMemory,
RowPerIndexPage=>\@RowPerIndexPage,
IndexMemory=>\@IndexMemory,
};
}
$template->param(tables => \@table_size);
print $template->output;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
<meta name="keywords" content="MySQL Cluster" />
<title>MySQL Cluster size estimate for <TMPL_VAR NAME="db" ESCAPE="HTML"></title>
<style type="text/css">
table { border-collapse: collapse }
td,th { border: 1px solid black }
</style>
</head>
<body>
<h1>MySQL Cluster analysis for <TMPL_VAR NAME="db" escape="html"></h1>
<p>This is an automated analysis of the <TMPL_VAR NAME="DSN" escape="html"> database for migration into <a href="http://www.mysql.com/">MySQL</a> Cluster. No warranty is made to the accuracy of the information.</p>
<p>This information should be valid for MySQL 4.1</p>
<ul>
<TMPL_LOOP NAME="tables">
<li><TMPL_VAR NAME="table"></li>
</TMPL_LOOP>
</ul>
<hr/>
<TMPL_LOOP NAME="tables">
<h2><TMPL_VAR NAME="table"></h2>
<table>
<tr>
<th>Column</th>
<th>Type</th>
<th>Size</th>
<th>Key</th>
<TMPL_LOOP NAME=releases>
<th><TMPL_VAR NAME=rel> NDB Size</th>
</TMPL_LOOP>
</tr>
<TMPL_LOOP NAME="columns">
<tr>
<td><TMPL_VAR NAME=name></td>
<td><TMPL_VAR NAME=type></td>
<td><TMPL_VAR NAME=size></td>
<td><TMPL_VAR NAME=key></td>
<TMPL_LOOP NAME=datamemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
</TMPL_LOOP>
</table>
<p>&nbsp;</p>
<h3>Indexes</h3>
<p>We assume that indexes are ORDERED (not created USING HASH). If order is not required, 10 bytes of data memory can be saved per row if the index is created USING HASH</p>
<table>
<tr>
<th>Index</th>
<th>Type</th>
<th>Columns</th>
<TMPL_LOOP NAME=releases>
<th><TMPL_VAR NAME=rel> IdxMem</th>
</TMPL_LOOP>
<TMPL_LOOP NAME=releases>
<th><TMPL_VAR NAME=rel> DatMem</th>
</TMPL_LOOP>
</tr>
<TMPL_LOOP NAME="indexes">
<tr>
<td><TMPL_VAR NAME=name></td>
<td><TMPL_VAR NAME=type></td>
<td><TMPL_VAR NAME=columns></td>
<TMPL_LOOP NAME=indexmemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
<TMPL_LOOP NAME=datamemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
</TMPL_LOOP>
</table>
<h3>DataMemory Usage</h3>
<table>
<tr>
<th>&nbsp;</th>
<TMPL_LOOP NAME=releases>
<th><TMPL_VAR NAME=rel></th>
</TMPL_LOOP>
</tr>
<tr>
<th>Row Overhead</th>
<TMPL_LOOP NAME=releases>
<td>16</td>
</TMPL_LOOP>
</tr>
<tr>
<th>Column DataMemory/Row</th>
<TMPL_LOOP NAME=RowDataMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Index DataMemory/Row</th>
<TMPL_LOOP NAME=IndexDataMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Total DataMemory/Row</th>
<TMPL_LOOP NAME=TotalDataMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Rows per 32kb page</th>
<TMPL_LOOP NAME=RowPerPage>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Current number of rows</th>
<TMPL_LOOP NAME=count>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Total DataMemory (kb)</th>
<TMPL_LOOP NAME=DataMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
</table>
<h3>IndexMemory Usage</h3>
<table>
<tr>
<th>&nbsp;</th>
<TMPL_LOOP NAME=releases>
<th><TMPL_VAR NAME=rel></th>
</TMPL_LOOP>
</tr>
<tr>
<th>IndexMemory/Row</th>
<TMPL_LOOP NAME=RowIndexMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Rows per 8kb page</th>
<TMPL_LOOP NAME=RowPerIndexPage>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Current number of rows</th>
<TMPL_LOOP NAME=count>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
<tr>
<th>Total IndexMemory (kb)</th>
<TMPL_LOOP NAME=IndexMemory>
<td><TMPL_VAR NAME=val></td>
</TMPL_LOOP>
</tr>
</table>
<hr/>
</TMPL_LOOP>
<p>This is the output of ndb_size.pl.</p>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment