Browse files

framework-style new codebase

git-svn-id: svn+ssh://code.etolabo.org/usr/etolabo/var/svn/ficia/kumofs/branches/framework@1028 b300df90-00d7-44b1-9271-3fa3ef26909a
  • Loading branch information...
1 parent 5abc2b4 commit ba2d215eb47dfe59433ef67219773703f9782c4e frsyuki committed Mar 12, 2009
Showing with 19,841 additions and 0 deletions.
  1. +1 −0 AUTHORS
  2. +14 −0 COPYING
  3. 0 ChangeLog
  4. +237 −0 INSTALL
  5. +25 −0 Makefile.am
  6. 0 NEWS
  7. +14 −0 README
  8. +113 −0 bootstrap
  9. +172 −0 configure.in
  10. +41 −0 doc/Makefile
  11. +34 −0 doc/kumoctl.1
  12. +20 −0 doc/kumoctl.1.txt
  13. +6 −0 doc/kumolog.1
  14. +9 −0 doc/kumolog.1.txt
  15. +31 −0 doc/kumostat.1
  16. +19 −0 doc/kumostat.1.txt
  17. +469 −0 doc/memo.html
  18. BIN doc/memo.pdf
  19. +401 −0 doc/memo.txt
  20. +6 −0 src/Makefile.am
  21. +16 −0 src/command/Makefile.am
  22. +285 −0 src/command/kumoctl
  23. +212 −0 src/command/kumolog
  24. +90 −0 src/command/kumostat
  25. +86 −0 src/command/mergedb.cc
  26. +9 −0 src/kazuhiki/Makefile.am
  27. +630 −0 src/kazuhiki/kazuhiki.cc
  28. +242 −0 src/kazuhiki/kazuhiki.h
  29. +38 −0 src/log/Makefile.am
  30. +278 −0 src/log/logpack.c
  31. +55 −0 src/log/logpack.h
  32. +120 −0 src/log/logpack.hpp.erb
  33. +14 −0 src/log/logpacker.cc
  34. +33 −0 src/log/logpacker.h
  35. +21 −0 src/log/mlogger.cc
  36. +150 −0 src/log/mlogger.h.erb
  37. +13 −0 src/log/mlogger_null.cc
  38. +15 −0 src/log/mlogger_null.h
  39. +16 −0 src/log/mlogger_ostream.cc
  40. +18 −0 src/log/mlogger_ostream.h
  41. +39 −0 src/log/mlogger_syslog.cc
  42. +16 −0 src/log/mlogger_syslog.h
  43. +62 −0 src/log/mlogger_tty.cc
  44. +18 −0 src/log/mlogger_tty.h
  45. +152 −0 src/logic/Makefile.am
  46. +211 −0 src/logic/boot.cc
  47. +108 −0 src/logic/boot.h
  48. +31 −0 src/logic/client_logic.h
  49. +155 −0 src/logic/clock.h
  50. +76 −0 src/logic/cluster_logic.h
  51. +31 −0 src/logic/gateway.proto.h
  52. +80 −0 src/logic/gateway/framework.cc
  53. +102 −0 src/logic/gateway/framework.h
  54. +488 −0 src/logic/gateway/gate_cloudy.cc
  55. +30 −0 src/logic/gateway/gate_cloudy.h
  56. +697 −0 src/logic/gateway/gate_memproto.cc
  57. +30 −0 src/logic/gateway/gate_memproto.h
  58. +487 −0 src/logic/gateway/gate_memtext.cc
  59. +30 −0 src/logic/gateway/gate_memtext.h
  60. +49 −0 src/logic/gateway/init.h
  61. +23 −0 src/logic/gateway/interface.cc
  62. +90 −0 src/logic/gateway/interface.h
  63. +157 −0 src/logic/gateway/main.cc
  64. +247 −0 src/logic/gateway/memproto/memproto.c
  65. +183 −0 src/logic/gateway/memproto/memproto.h
  66. +164 −0 src/logic/gateway/memproto/memtext.h
  67. +342 −0 src/logic/gateway/memproto/memtext.rl
  68. +84 −0 src/logic/gateway/proto_network.cc
  69. +332 −0 src/logic/gateway/scope_store.cc
  70. +59 −0 src/logic/gateway/scope_store.h
  71. +28 −0 src/logic/global.h
  72. +137 −0 src/logic/hash.cc
  73. +299 −0 src/logic/hash.h
  74. +189 −0 src/logic/manager.proto.h
  75. +51 −0 src/logic/manager/control_framework.cc
  76. +37 −0 src/logic/manager/control_framework.h
  77. +96 −0 src/logic/manager/framework.cc
  78. +128 −0 src/logic/manager/framework.h
  79. +49 −0 src/logic/manager/init.h
  80. +85 −0 src/logic/manager/main.cc
  81. +116 −0 src/logic/manager/proto_control.cc
  82. +238 −0 src/logic/manager/proto_network.cc
  83. +385 −0 src/logic/manager/proto_replace.cc
  84. +146 −0 src/logic/msgtype.h
  85. +9 −0 src/logic/protogen/Makefile.am
  86. +408 −0 src/logic/protogen/protogen
  87. +14 −0 src/logic/role.h
  88. +120 −0 src/logic/rpc_server.h
  89. +294 −0 src/logic/server.proto.h
  90. +146 −0 src/logic/server/buffer_queue.h
  91. +89 −0 src/logic/server/framework.cc
  92. +162 −0 src/logic/server/framework.h
  93. +66 −0 src/logic/server/init.h
  94. +142 −0 src/logic/server/main.cc
  95. +68 −0 src/logic/server/proto_control.cc
  96. +186 −0 src/logic/server/proto_network.cc
  97. +369 −0 src/logic/server/proto_replace.cc
  98. +639 −0 src/logic/server/proto_replace_stream.cc
  99. +569 −0 src/logic/server/proto_store.cc
  100. +294 −0 src/logic/server/storage.cc
  101. +264 −0 src/logic/server/storage.h
  102. +99 −0 src/logic/server/storage/interface.h
  103. +393 −0 src/logic/server/storage/luxio.cc
  104. +349 −0 src/logic/server/storage/tchdb.cc
  105. +94 −0 src/logic/wavy_server.cc
  106. +43 −0 src/logic/wavy_server.h
  107. +82 −0 src/mp/Makefile.am
  108. +45 −0 src/mp/exception.h
  109. +64 −0 src/mp/functional.h
  110. +67 −0 src/mp/memory.h
  111. +94 −0 src/mp/object_callback.pre.h
  112. +14 −0 src/mp/pp.h
  113. +196 −0 src/mp/pthread.h
  114. +393 −0 src/mp/pthread_impl.h
  115. +72 −0 src/mp/shared_buffer.h
  116. +167 −0 src/mp/shared_buffer_impl.h
  117. +83 −0 src/mp/source.h
  118. +143 −0 src/mp/source_impl.h
  119. +79 −0 src/mp/stream_buffer.h
  120. +203 −0 src/mp/stream_buffer_impl.h
  121. +50 −0 src/mp/utility.h
  122. +26 −0 src/mp/wavy.h
  123. +138 −0 src/mp/wavy/core.pre.h
  124. +73 −0 src/mp/wavy/output.h
  125. +214 −0 src/mp/wavy/singleton.pre.h
  126. +17 −0 src/mpsrc/Makefile.am
  127. +145 −0 src/mpsrc/wavy_connect.cc
  128. +212 −0 src/mpsrc/wavy_core.cc
  129. +91 −0 src/mpsrc/wavy_core.h
  130. +38 −0 src/mpsrc/wavy_edge.h
  131. +126 −0 src/mpsrc/wavy_edge_epoll.h
  132. +124 −0 src/mpsrc/wavy_edge_kqueue.h
  133. +72 −0 src/mpsrc/wavy_listen.cc
  134. +502 −0 src/mpsrc/wavy_output.cc
  135. +59 −0 src/mpsrc/wavy_timer.cc
  136. +40 −0 src/rpc/Makefile.am
  137. +106 −0 src/rpc/address.cc
  138. +213 −0 src/rpc/address.h
  139. +97 −0 src/rpc/client.h
  140. +219 −0 src/rpc/client_tmpl.h
  141. +250 −0 src/rpc/cluster.cc
Sorry, we could not display the entire diff because it was too big.
View
1 AUTHORS
@@ -0,0 +1 @@
+FURUHASHI Sadayuki <frsyuki _at_ users.sourceforge.jp>
View
14 COPYING
@@ -0,0 +1,14 @@
+Copyright (C) 2008 FURUHASHI Sadayuki
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
View
0 ChangeLog
No changes.
View
237 INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
View
25 Makefile.am
@@ -0,0 +1,25 @@
+export ERB
+export RUBY
+export RAGEL
+SUBDIRS = src
+
+DOC_WFDOC = \
+ doc/memo.txt \
+ doc/kumoctl.1.txt \
+ doc/kumolog.1.txt \
+ doc/kumostat.1.txt
+
+DOC_FILES = \
+ doc/memo.pdf \
+ doc/memo.html \
+ doc/kumoctl.1 \
+ doc/kumolog.1 \
+ doc/kumostat.1
+
+EXTRA_DIST = $(DOC_WFDOC) $(DOC_FILES)
+
+man_MANS = \
+ doc/kumoctl.1 \
+ doc/kumolog.1 \
+ doc/kumostat.1
+
View
0 NEWS
No changes.
View
14 README
@@ -0,0 +1,14 @@
+Copyright (C) 2008 FURUHASHI Sadayuki
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
View
113 bootstrap
@@ -0,0 +1,113 @@
+#!/bin/sh
+# vim:ts=4:sw=4
+# Calls autotools to build configure script and Makefile.in.
+# Generated automatically using bootstrapper 0.2.1
+# http://bootstrapper.sourceforge.net/
+#
+# Copyright (C) 2002 Anthony Ventimiglia
+#
+# This bootstrap script is free software; you can redistribute
+# it and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+#
+# Calls proper programs to create configure script and Makefile.in files.
+# if run with the --clean option, bootstrap removes files it generates. To
+# clean all autogenerated files (eg: for cvs imports) first run
+# make distclean, then bootstrap --clean
+# see bootstrapper(1) for more infor
+
+
+if test x"$1" = x"--help"; then
+ echo "$0: automatic bootstrapping utility for GNU Autotools"
+ echo " cleans up old autogenerated files and runs autoconf,"
+ echo " automake and aclocal on local directory"
+ echo
+ echo " --clean clean up auto-generated files without"
+ echo " creating new scripts"
+ echo
+ exit 0
+fi
+
+
+ACLOCAL="aclocal"
+ACLOCAL_FILES="aclocal.m4"
+ALWAYS_CLEAN="config.status config.log config.cache libtool"
+AUTOCONF="autoconf"
+AUTOCONF_FILES="configure"
+AUTOHEADER="autoheader"
+AUTOHEADER_FILES=""
+AUTOMAKE="automake --add-missing --copy"
+AUTOMAKE_FILES="config.sub stamp-h.in ltmain.sh missing mkinstalldirs install-sh config.guess"
+CONFIG_AUX_DIR="."
+CONFIG_FILES="stamp-h ltconfig"
+CONFIG_HEADER=""
+if [ x`uname` = x"Darwin" ]; then
+ LIBTOOLIZE="glibtoolize --force --copy"
+else
+ LIBTOOLIZE="libtoolize --force --copy"
+fi
+LIBTOOLIZE_FILES="config.sub ltmain.sh config.guess"
+RM="rm"
+SUBDIRS="[]"
+
+
+# These are files created by configure, so we'll always clean them
+for i in $ALWAYS_CLEAN; do
+ test -f $i && \
+ $RM $i
+done
+
+if test x"$1" = x"--clean"; then
+ #
+ #Clean Files left by previous bootstrap run
+ #
+ if test -n "$CONFIG_AUX_DIR";
+ then CONFIG_AUX_DIR="$CONFIG_AUX_DIR/"
+ fi
+ # Clean Libtoolize generated files
+ for cf in $LIBTOOLIZE_FILES; do
+ cf="$CONFIG_AUX_DIR$cf"
+ test -f $cf && \
+ $RM $cf
+ done
+ #aclocal.m4 created by aclocal
+ test -f $ACLOCAL_FILES && $RM $ACLOCAL_FILES
+ #Clean Autoheader Generated files
+ for cf in $AUTOHEADER_FILES; do
+ cf=$CONFIG_AUX_DIR$cf
+ test -f $cf && \
+ $RM $cf
+ done
+ # remove config header (Usaually config.h)
+ test -n "$CONFIG_HEADER" && test -f $CONFIG_HEADER && $RM $CONFIG_HEADER
+ #Clean Automake generated files
+ for cf in $AUTOMAKE_FILES; do
+ cf=$CONFIG_AUX_DIR$cf
+ test -f $cf && \
+ $RM $cf
+ done
+ for i in $SUBDIRS; do
+ test -f $i/Makefile.in && \
+ $RM $i/Makefile.in
+ done
+ #Autoconf generated files
+ for cf in $AUTOCONF_FILES; do
+ test -f $cf && \
+ $RM $cf
+ done
+ for cf in $CONFIG_FILES; do
+ cf="$CONFIG_AUX_DIR$cf"
+ test -f $cf && \
+ $RM $cf
+ done
+else
+ $LIBTOOLIZE
+ $ACLOCAL
+ $AUTOHEADER
+ $AUTOMAKE
+ $AUTOCONF
+fi
+
+
View
172 configure.in
@@ -0,0 +1,172 @@
+AC_INIT(src/logic/boot.h)
+AC_CONFIG_AUX_DIR(ac)
+AM_INIT_AUTOMAKE(kumofs, 0.2.0)
+AC_CONFIG_HEADER(config.h)
+
+
+AC_SUBST(CFLAGS)
+if test "" = "$CFLAGS"; then
+ CFLAGS="-O4"
+fi
+CFLAGS="-O4 -Wall $CFLAGS"
+
+
+AC_SUBST(CXXFLAGS)
+if test "" = "$CXXFLAGS"; then
+ CXXFLAGS="-O4"
+fi
+CXXFLAGS="-O4 -Wall $CXXFLAGS"
+
+
+AC_CHECK_PROG(RUBY, ruby, ruby)
+if test "x$RUBY" = x; then
+ AC_MSG_ERROR([cannot find ruby. Ruby is needed to build.])
+fi
+
+AC_CHECK_PROG(ERB, erb, erb)
+if test "x$ERB" = x; then
+ AC_MSG_ERROR([cannot find erb. Ruby is needed to build.])
+fi
+
+AC_CHECK_PROG(RAGEL, ragel, ragel)
+if test "x$RAGEL" = x; then
+ AC_MSG_ERROR([cannot find ragel. Ragel is needed to build.])
+fi
+
+
+AC_PROG_CC
+AC_PROG_CXX
+
+AC_PROG_LIBTOOL
+AM_PROG_AS
+AM_PROG_CC_C_O
+
+
+AC_ARG_WITH([msgpack],
+ AS_HELP_STRING([--with-msgpack=DIR],
+ [specify the root directory for msgpack library]),
+ [msgpack_path="$withval"], [])
+if test "$msgpack_path" != ""; then
+ CXXFLAGS="$CXXFLAGS -I$msgpack_path/include"
+ CFLAGS="$CFLAGS -I$msgpack_path/include"
+ LDFLAGS="$LDFLAGS -L$msgpack_path/lib"
+fi
+
+
+storage_type="tokyocabinet"
+
+
+AC_ARG_WITH([tokyocabinet],
+ AS_HELP_STRING([--with-tokyocabinet@<:@=DIR@:>@],
+ [use Tokyo Cabinet for the backend storage]),
+ [tokyocabinet_path="$withval"], [])
+if test "$tokyocabinet_path" != ""; then
+ storage_type="tokyocabinet"
+ CXXFLAGS="$CXXFLAGS -I$tokyocabinet_path/include"
+ CFLAGS="$CFLAGS -I$tokyocabinet_path/include"
+ LDFLAGS="$LDFLAGS -L$tokyocabinet_path/lib"
+fi
+
+
+#AC_ARG_WITH([luxio],
+# AS_HELP_STRING([--with-luxio@<:@=DIR@:>@],
+# [use LuxIO for the backend storage (NOT IMPLEMENTED)]),
+# [luxio_path="$withval"], [])
+#if test "$luxio_path" != ""; then
+# storage_type="luxio"
+# CXXFLAGS="$CXXFLAGS -I$luxio_path/include"
+# CFLAGS="$CFLAGS -I$luxio_path/include"
+# LDFLAGS="$LDFLAGS -L$luxio_path/lib"
+#fi
+
+
+AC_MSG_CHECKING([if tcmalloc is enabled])
+AC_ARG_WITH([tcmalloc],
+ AS_HELP_STRING([--with-tcmalloc@<:@=DIR@:>@],
+ [link libtcmalloc]),
+ [with_tcmalloc="$withval"], [with_tcmalloc="no"])
+AC_MSG_RESULT($with_tcmalloc)
+if test "$with_tcmalloc" != "no"; then
+ if test "$with_tcmalloc" != "yes"; then
+ LDFLAGS="$LDFLAGS -L$with_tcmalloc/lib"
+ LDFLAGS="$LDFLAGS -L$with_tcmalloc/lib64"
+ fi
+ AC_CHECK_LIB(tcmalloc,malloc,,
+ AC_MSG_ERROR([Can't find tcmalloc library]))
+fi
+
+
+AC_CHECK_LIB(stdc++, main)
+
+AC_CHECK_LIB(pthread,pthread_create,,
+ AC_MSG_ERROR([Can't find pthread library]))
+
+AC_CHECK_LIB(z,deflate,,
+ AC_MSG_ERROR([Can't find zlib library]))
+
+AC_CHECK_HEADERS(openssl/sha.h,,
+ AC_MSG_ERROR([Can't find openssl header]))
+AC_CHECK_LIB(crypto,SHA1,,
+ AC_MSG_ERROR([Can't find openssl library]))
+
+AC_CHECK_LIB(msgpack,main,,
+ AC_MSG_ERROR([Can't find msgpack library]))
+
+AC_MSG_CHECKING([storage backend])
+AC_MSG_RESULT($storage_type)
+AM_CONDITIONAL(USE_TOKYOCABINET, test "$storage_type" = "tokyocabinet")
+if test "$storage_type" = "tokyocabinet"; then
+ AC_CHECK_HEADERS(tchdb.h,,
+ AC_MSG_ERROR([Can't find tokyo cabinet header]))
+ AC_CHECK_LIB(tokyocabinet,tchdbget,,
+ AC_MSG_ERROR([Can't find tokyocabinet library]))
+ CXXFLAGS="$CXXFLAGS -DUSE_TOKYOCABINET"
+ CFLAGS="$CFLAGS -DUSE_TOKYOCABINET"
+else
+ AC_LANG_PUSH(C++)
+ AC_CHECK_HEADERS(luxio/btree.h) # FIXME luxio UINT8_MAX
+ #AC_CHECK_HEADERS(luxio/btree.h,,
+ # AC_MSG_ERROR([Can't find luxio header]))
+ AC_LANG_POP
+fi
+
+
+
+AC_MSG_CHECKING([if debug option is enabled])
+AC_ARG_ENABLE(debug,
+ AS_HELP_STRING([--disable-debug],
+ [disable assert macros and omit -g option.]) )
+if test "$enable_debug" != "no"; then
+ CXXFLAGS="$CXXFLAGS -g"
+ CFLAGS="$CFLAGS -g"
+else
+ CXXFLAGS="$CXXFLAGS -DNDEBUG"
+ CFLAGS="$CFLAGS -DNDEBUG"
+fi
+AC_MSG_RESULT($enable_debug)
+
+
+AC_MSG_CHECKING([if trace message is enabled])
+AC_ARG_ENABLE(trace,
+ AS_HELP_STRING([--enable-trace], [enable trace messages.]) )
+if test "$enable_trace" = "yes"; then
+ CXXFLAGS="$CXXFLAGS -DMLOGGER_LEVEL=0"
+ CFLAGS="$CFLAGS -DMLOGGER_LEVEL=0"
+else
+ CXXFLAGS="$CXXFLAGS -DMLOGGER_LEVEL=2"
+ CFLAGS="$CFLAGS -DMLOGGER_LEVEL=2"
+fi
+AC_MSG_RESULT($enable_trace)
+
+
+AC_OUTPUT([src/mp/Makefile
+ src/mpsrc/Makefile
+ src/log/Makefile
+ src/kazuhiki/Makefile
+ src/rpc/Makefile
+ src/logic/protogen/Makefile
+ src/logic/Makefile
+ src/command/Makefile
+ src/Makefile
+ Makefile])
+
View
41 doc/Makefile
@@ -0,0 +1,41 @@
+#SRCS=$(wildcard *txt)
+#default: $(SRCS:.txt=.pdf) $(SRCS:.txt=.html)
+#clean: $(SRCS:.txt=.clean)
+#distclean: $(SRCS:.txt=.distclean)
+
+default: memo.pdf memo.html kumoctl.1 kumostat.1 kumolog.1
+clean: memo.clean
+distclean: memo.distclean
+
+
+%.sdoc : %.txt
+ wfdoc -i $< -o $@
+
+%.html : %.txt
+ wfdoc -f html -i $< -o $@
+
+%.tex : %.sdoc
+ sdoc -toc -format:latex2e -latex2e.driver=dvipdfm -latex2e.ref=HyperRefLaTeX2eRefHandler $<
+
+%.etex : %.tex
+ wftex $< $@
+
+%.toc : %.etex
+ platex $<
+
+%.dvi : %.etex %.toc
+ platex $<
+
+%.pdf : %.dvi
+ dvipdfmx $<
+ rm -f $*.aux $*.log
+
+%.clean : %.txt
+ rm -f $*.sdoc $*.tex $*.etex $*.dvi $*.toc $*.aux $*.out $*.log $*.pdf
+
+%.distclean : %.txt
+ rm -f $*.sdoc $*.tex $*.etex $*.dvi $*.toc $*.aux $*.out $*.log $*.pdf $*.mk
+
+%.1 : %.1.txt
+ wfdoc -f man $< -o $@
+
View
34 doc/kumoctl.1
@@ -0,0 +1,34 @@
+.TH kumoctl
+.SH NAME
+kumoctl
+.SH SYNOPSIS
+kumoctl address[:port=19750] command [options]
+.SS COMMANDS
+.TP
+.B status
+get status
+.TP
+.B attach
+attach all new servers and start replace
+.TP
+.B attach-noreplace
+attach all new servers
+.TP
+.B detach
+detach all fault servers and start replace
+.TP
+.B detach-noreplace
+detach all fault servers
+.TP
+.B replace
+start replace without attach/detach
+.TP
+.B backup [suffix=20090304]
+create backup with specified suffix
+.TP
+.B enable-auto-replace
+enable auto replace
+.TP
+.B disable-auto-replace
+disable auto replace
+.SH DESCRIPTION
View
20 doc/kumoctl.1.txt
@@ -0,0 +1,20 @@
+*? kumoctl
+*NAME
+kumoctl
+
+*SYNOPSIS
+kumoctl address[:port=19750] command [options]
+
+**COMMANDS
+:status :get status
+:attach :attach all new servers and start replace
+:attach-noreplace :attach all new servers
+:detach :detach all fault servers and start replace
+:detach-noreplace :detach all fault servers
+:replace :start replace without attach/detach
+:backup [suffix=20090304] :create backup with specified suffix
+:enable-auto-replace :enable auto replace
+:disable-auto-replace :disable auto replace
+
+*DESCRIPTION
+
View
6 doc/kumolog.1
@@ -0,0 +1,6 @@
+.TH kumolog
+.SH NAME
+kumolog
+.SH SYNOPSIS
+kumolog <logfile.mpac>
+.SH DESCRIPTION
View
9 doc/kumolog.1.txt
@@ -0,0 +1,9 @@
+*? kumolog
+*NAME
+kumolog
+
+*SYNOPSIS
+kumolog <logfile.mpac>
+
+*DESCRIPTION
+
View
31 doc/kumostat.1
@@ -0,0 +1,31 @@
+.TH kumostat
+.SH NAME
+kumostat
+.SH SYNOPSIS
+kumostat address[:port=19800] command [options]
+.SS COMMANDS
+.TP
+.B pid
+get pid of server process
+.TP
+.B uptime
+get uptime
+.TP
+.B time
+get UNIX time
+.TP
+.B version
+get version
+.TP
+.B cmd_get
+get number of get requests
+.TP
+.B cmd_set
+get number of set requests
+.TP
+.B cmd_delete
+get number of delete requests
+.TP
+.B items
+get number of stored items
+.SH DESCRIPTION
View
19 doc/kumostat.1.txt
@@ -0,0 +1,19 @@
+*? kumostat
+*NAME
+kumostat
+
+*SYNOPSIS
+kumostat address[:port=19800] command [options]
+
+**COMMANDS
+:pid :get pid of server process
+:uptime :get uptime
+:time :get UNIX time
+:version :get version
+:cmd_get :get number of get requests
+:cmd_set :get number of set requests
+:cmd_delete :get number of delete requests
+:items :get number of stored items
+
+*DESCRIPTION
+
View
469 doc/memo.html
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta content="application/xhtml+xml; charset=UTF-8" http-equiv="content-type" />
+ <meta content="text/css" http-equiv="content-style-type" />
+ <meta content="text/javascript" http-equiv="content-script-type" />
+ <title>Kumofs memo -- Kumo Fast Storage rev.948</title>
+ <meta name="author" content="FURUHASHI Sadayuki" />
+ </head>
+ <body>
+ <h2>概要</h2>
+ <p>kumofsはkey-value型のデータを保存する分散ストレージ。key=&gt;valueを保存する<strong>set</strong>、keyを取得する<strong>get</strong>、keyを削除する<strong>delete</strong>の3つの操作をサポートする。</p>
+ <p>データを保持する<strong>Server</strong>、Server群を管理する<strong>Manager</strong>、アプリケーションからのリクエストをServerに中継する<strong>Gateway</strong>の3種類のノードでシステムを構成する。</p>
+ <p>データは複数のServerに分散して保存されるため、Serverを追加するほど性能が向上する。</p>
+ <p>データは3台のServerにコピーされて保存される。2台までならServerがダウンしても動作し続ける。</p>
+ <p>Server群はManagerによって死活監視されている。Serverがダウンしたら、そのServerは直ちにシステムから切り離される。ただし1台か2台のManagerが起動していないとServerの切り離しが行われないので、Managerが1台も起動していない状態でServerがダウンするとシステムが停止してしまう。</p>
+ <p>Serverを追加したり切り離したりした後、その状態をシステムに反映するには、レプリケーションされたデータの再配置を行う必要がある。これは自動では行われず、<strong>kumoctlコマンド</strong>を使って手動で行う。</p>
+ <br />
+ <h3>Consistent Hashing</h3>
+ <p>Consistent Hashingを使ってデータを分散して保存する<sup class="footnote">
+ <a title="ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台" href="#footnote4-1" id="footnote4-1-anchor">*1</a>
+ </sup>。</p>
+ <p>Serverがダウンしたときは、そのServerの仮想ノードに<strong>faultフラグ</strong>がセットされる。set/get/deleteはfaultフラグがセットされたServerをスキップして行われる。つまり、通常動作時はレプリケーションは3つ作成されるが、1台がfault状態ならコピーは2つ、2台がfault状態ならコピーは1つしか作成されないkeyが存在することになる。fault状態のServerが3台以上になると、get/set/deleteが失敗し続けるkeyが存在することになる。</p>
+ <p>Serverがダウンしてもfaultフラグがセットされるだけで、レプリケーションの再配置は行われない。faultフラグがセットされたServerが存在する状態で、kumoctlコマンドを使って<strong>detach</strong>コマンドをManagerに送信すると、faultフラグがセットされたServerがハッシュ空間から取り除かれる。同時にレプリケーションの再配置が行われ、すべてのkeyに対してレプリケーションが3つ作成されるようにデータがコピーされる。</p>
+ <p>Serverが追加されてもすぐにはハッシュ空間には追加されず、レプリケーションの再配置は行われない。新たなServerが起動している状態で、kumoctlコマンドを使って<strong>attach</strong>コマンドをManagerに送信すると、新しいServerがハッシュ空間に追加される。同時にレプリケーションの再配置が行われ、すべてのkeyに対してレプリケーションが3つだけ存在するようにデータが移動される。</p>
+ <p># TODO: auto-replace</p>
+ <h3>set/get/deleteの保証範囲</h3>
+ <h4>set(key, value)</h4>
+ <p>key=&gt;valueを保存する。保存できれば成功を返す。保存できなければエラーを返す。</p>
+ <p>既にkeyが保存されていたとき、setが成功した場合はkeyの値は確実に上書きされている。</p>
+ <p>setが失敗したとき、keyの値は不定になっている。これは失敗したときにロールバックを行わないため。ロールバックを一貫性を損なうことなく行うための高級なアルゴリズムは実装されていない/使うと性能が低下してしまう。</p>
+ <p>Serverはレプリケーション先の2台〜0台のすべてのServerにデータが受信されたことを確認してからGatewayにレスポンスを返す。どれか1台でもコピー処理が失敗したらエラーを返す。つまり、アプリケーションに成功が返されたときはfault状態でないすべてのServerにレプリケーションがコピーされており、それ以降に古いデータが読み出されることはない。ただしディスクに書き込まれているとは限らない。</p>
+ <h4>get(key)</h4>
+ <p>keyをsetするリクエストが成功していた場合は、そのkeyに対応するvalueを返す。setが失敗していた場合は、nullか、setに失敗したvalueが返る。それ以外であればnullを返す。</p>
+ <p>keyをsetするリクエストが成功してvalueが保存されていたとしても、レプリケーションされたすべてのServerの負荷が非常に高いために応答できない場合は、getがタイムアウトする可能性がある。</p>
+ <p>keyが保存されていなかった場合はエラーにならないが、タイムアウトした場合はエラーになる。</p>
+ <h4>delete(key)</h4>
+ <p>keyを削除する。</p>
+ <p>再配置処理を行っている間にdeleteを行うと、削除されないことがある。また同じkeyに対してdeleteとsetをほぼ同時に行うと、削除されないことがある。これはレプリケーションを行うServer同士のやりとりが、Gatewayが送出したdeleteリクエストと入れ違う可能性があるため。</p>
+ <p>Serverの引数を調整することで、deleteが一貫性を保たない確率を減らすことができる。</p>
+ <br />
+ <h3>動作環境と制限</h3>
+ <h4>サーバーの時刻設定</h4>
+ <p>ManagerとServerを動作させるホストの時刻設定は、TIME_ERROR_MARGIN秒(コンパイル時に決定。デフォルトでは5秒)以上ずれていると正常に動作しない。またUTCとlocaltimeはどちらかに揃える必要がある。</p>
+ <p># TODO</p>
+ <br />
+ <div class="footnote_view">
+ <p>
+ <a href="#footnote4-1-anchor" id="footnote4-1">*1</a>ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台</p>
+ </div>
+ <br />
+ <h2>インストールと実行</h2>
+ <h3>依存関係</h3>
+ <h4>動作環境</h4>
+ <ul class="ul1">
+ <li>linux &gt;= 2.6.18</li>
+ <li>glibc &gt;= XXX</li>
+ </ul>
+ <h4>コンパイル時に必要なもの</h4>
+ <ul class="ul1">
+ <li>g++ &gt;= 4.1</li>
+ <li>ragel &gt;= 6.3</li>
+ <li>git &gt;= XXX</li>
+ </ul>
+ <h4>コンパイル時と実行時に必要なもの</h4>
+ <ul class="ul1">
+ <li>ruby &gt;= 1.8</li>
+ <li>rubygems</li>
+ <li>libcrypto(openssl)</li>
+ <li>zlib &gt;= XXX</li>
+ <li>Tokyo Cabinet &gt;= 1.4.9</li>
+ </ul>
+ <h3>コンパイル</h3>
+ <p>まず最新のMessagePackをインストールする。</p>
+ <pre class="code_sh">$ git clone git://git.sourceforge.jp/gitroot/msgpack/msgpack.git
+$ cd msgpack
+$ ./bootstrap &amp;&amp; ./configure &amp;&amp; make
+$ sudo make install
+$ cd ruby
+$ ./gengem
+$ sudo gem install gem/pkg/msgpack-*.gem
+</pre>
+ <p class="title">MessagePackのインストール</p>
+ <p>次にkumofsをインストールする。</p>
+ <pre class="code_sh">$ ./configure &amp;&amp; make &amp;&amp; make install
+</pre>
+ <p>以下の4つのコマンドがインストールされる:</p>
+ <dl>
+ <dt>kumo-manager</dt>
+ <dd>Managerノード。Serverノードの管理をする。</dd>
+ <dt>kumo-server</dt>
+ <dd>Serverノード。実際にデータを保存する。</dd>
+ <dt>kumo-gateway</dt>
+ <dd>Gatewayノード。memcachedプロトコルのサーバーで、アプリケーションからの要求をServerノードに中継する。</dd>
+ <dt>kumoctl</dt>
+ <dd>Managerノードを制御するための管理コマンド</dd>
+ <dt>kumolog</dt>
+ <dd>バイナリフォーマットのログをテキストフォーマットに変換する</dd>
+ <dt>kumomergedb</dt>
+ <dd>コールドバックアップファイルをマージする</dd>
+ </dl>
+ <h4>configureフラグ</h4>
+ <dl>
+ <dt>--with-msgpack=DIR</dt>
+ <dd>MessagePackがインストールされているディレクトリを指定する</dd>
+ <dt>--with-tokyocabinet=DIR</dt>
+ <dd>Tokyo Cabinetがインストールされているディレクトリを指定する</dd>
+ <dt>--enable-trace</dt>
+ <dd>画面を埋め尽くすほど冗長なデバッグ用のメッセージを出力するようにする</dd>
+ <dt>--with-tcmalloc[=DIR]</dt>
+ <dd>tcmallocとリンクする</dd>
+ </dl>
+ <h3>実行例</h3>
+ <h4>Manager 2台, Server 4台を使った冗長構成</h4>
+ <p>
+ <strong>s1</strong>〜<strong>s4</strong>の4台でクラスタを構成し、<strong>c1</strong>と<strong>c2</strong>で動作するアプリケーションから利用する例。</p>
+ <p>s1〜s4でServerを起動し、<strong>s1</strong>と<strong>s2</strong>では同時にManagerも起動する。<strong>c1</strong>と<strong>c2</strong>ではGatewayを起動する。</p>
+ <pre class="code_sh">[s1]$ kumo-manager -v -l s1 -p s2 # Manager同士は互いに指定する
+[s2]$ kumo-manager -v -l s2 -p s1 # Manager同士は互いに指定する
+[s1]$ kumo-server -v -m s1 -p s2 -l s1 -s database.tch # -mと-pでManagerを指定する
+[s2]$ kumo-server -v -m s1 -p s2 -l s2 -s database.tch # -lは常に自ホストのアドレス
+[s3]$ kumo-server -v -m s1 -p s2 -l s3 -s database.tch # -sはデータベース名
+[s4]$ kumo-server -v -m s1 -p s2 -l s4 -s database.tch # -vは冗長なメッセージを出力
+[c1]$ kumo-gateway -v -m s1 -p s2 -t 11211 # 11211/tcpでmemcachedテキストプロトコル
+[c2]$ kumo-gateway -v -m s1 -p s2 -t 11211 # を待ち受ける
+</pre>
+ <h4>localhostでクラスタを構成する</h4>
+ <p>localhostでManagerノード1台、Server 2台を使ってクラスタを構成する例。</p>
+ <pre class="code_sh">[localhost]$ kumo-manager -v -l localhost # Managerを1台で運用するときは-pを省略
+ # kumo-serverはポートを変えて起動する
+[localhost]$ kumo-server -v -m localhost -l localhost:19801 -L 19901 -s database1.tch
+[localhost]$ kumo-server -v -m localhost -l localhost:19802 -L 19902 -s database2.tch
+[localhost]$ kumo-gateway -v -m localhost -t 11211
+</pre>
+ <h3>主な引数</h3>
+ <h3>共通</h3>
+ <dl>
+ <dt>-o &lt;path.log&gt;</dt>
+ <dd>ログを標準出力ではなく指定されたファイルに出力する</dd>
+ <dt>-g &lt;path.mpac&gt;</dt>
+ <dd>バイナリログを指定されたファイルに出力する</dd>
+ <dt>-d &lt;path.pid&gt;</dt>
+ <dd>デーモンになる。指定されたファイルにpidを書き出す</dd>
+ <dt>-v</dt>
+ <dd>WARNよりレベルの低いメッセージを出力する</dd>
+ <dt>-Ci &lt;sec&gt;</dt>
+ <dd>タイマークロックの間隔を秒で指定する。小数を指定できる</dd>
+ <dt>-Ys &lt;sec&gt;</dt>
+ <dd>connect(2)のタイムアウト時間を秒で指定する。小数を指定できる</dd>
+ <dt>-Yn &lt;num&gt;</dt>
+ <dd>connect(2)のリトライ回数を指定する</dd>
+ <dt>-TR &lt;num&gt;</dt>
+ <dd>送信用スレッドの数を指定する</dd>
+ <dt>-TW &lt;num&gt;</dt>
+ <dd>受信用スレッドの数を指定する</dd>
+ </dl>
+ <h4>kumo-manager</h4>
+ <dl>
+ <dt>-l &lt;address&gt;</dt>
+ <dd>待ち受けるアドレス。<strong>他のノードから見て</strong>接続できるホスト名とポート番号を指定する</dd>
+ <dt>-p &lt;address&gt;</dt>
+ <dd>もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する</dd>
+ <dt>-c &lt;port&gt;</dt>
+ <dd>kumoctlからのコマンドを受け付けるポート番号を指定する</dd>
+ <dt>--auto-replace</dt>
+ <dd>Serverが追加・切断されたときに、マニュアル操作を待たずにレプリケーションの再配置を自動的に行うようにする。実行中でもkumoctlコマンドを使って変更できる</dd>
+ </dl>
+ <h4>kumo-server</h4>
+ <dl>
+ <dt>-l &lt;address&gt;</dt>
+ <dd>待ち受けるアドレス。<strong>他のノードから見て</strong>接続できるホスト名とポート番号を指定する</dd>
+ <dt>-L &lt;port&gt;</dt>
+ <dd>kumo-serverが待ち受けるもう一つのポートのポート番号を指定する</dd>
+ <dt>-m &lt;address&gt;</dt>
+ <dd>kumo-managerのホスト名とポート番号を指定する</dd>
+ <dt>-p &lt;address&gt;</dt>
+ <dd>もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する</dd>
+ <dt>-s &lt;path.tch&gt;</dt>
+ <dd>データを保存するデータベースファイルのパスを指定する</dd>
+ <dt>-f &lt;dir&gt;</dt>
+ <dd>レプリケーションの再配置に使う一時ファイルを保存するディレクトリを指定する。データベースファイルのサイズに応じて十分な空き容量が必要</dd>
+ <dt>-gS &lt;seconds&gt;</dt>
+ <dd>deleteしたエントリのクロックを保持しておくメモリ使用量の上限をKB単位で指定する</dd>
+ <dt>-gN &lt;seconds&gt;</dt>
+ <dd>deleteしたエントリのクロックを保持しておく最小時間を指定する。メモリ使用量が上限に達していると、最大時間に満たなくても最小時間を過ぎていれば削除される。</dd>
+ <dt>-gX &lt;seconds&gt;</dt>
+ <dd>deleteしたエントリのクロックを保持しておく最大時間を指定する</dd>
+ </dl>
+ <h4>kumo-gateway</h4>
+ <dl>
+ <dt>-m &lt;address&gt;</dt>
+ <dd>kumo-managerのホスト名とポート番号を指定する</dd>
+ <dt>-p &lt;address&gt;</dt>
+ <dd>もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する</dd>
+ <dt>-t &lt;port&gt;</dt>
+ <dd>memcachedテキストプロトコルを待ち受けるポート番号を指定する</dd>
+ <dt>-G &lt;number&gt;</dt>
+ <dd>getの最大リトライ回数を指定する</dd>
+ <dt>-S &lt;number&gt;</dt>
+ <dd>setの最大リトライ回数を指定する</dd>
+ <dt>-D &lt;number&gt;</dt>
+ <dd>deleteの最大リトライ回数を指定する</dd>
+ <dt>-As</dt>
+ <dd>set操作でレプリケーションするとき、レプリケーション完了の応答を待たずに成功を返すようにする</dd>
+ <dt>-Ad</dt>
+ <dd>delete操作でレプリケーションするとき、レプリケーション完了の応答を待たずに成功を返すようにする</dd>
+ </dl>
+ <div class="footnote_view">
+ <p>
+ <a href="#footnote4-1-anchor" id="footnote4-1">*1</a>ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台</p>
+ </div>
+ <br />
+ <h2>kumoctl</h2>
+ <p>kumoctlコマンドを使うとManagerの状態を取得したり、コマンドを送ったりできる。</p>
+ <p>Rubyで書かれたスクリプト。実行するにはgemでmsgpackパッケージをインストールする。</p>
+ <p>第1引数にManagerのホスト名とポート番号を指定し、第2引数にコマンドを指定する。</p>
+ <pre class="code_sh">$ kumoctl --help
+Usage: kumoctl address[:port=19799] command [options]
+command:
+ status get status
+ attach attach all new servers and start replace
+ attach-noreplace attach all new servers
+ detach detach all fault servers and start replace
+ detach-noreplace detach all fault servers
+ replace start replace without attach/detach
+ backup [suffix=????????] create backup with specified suffix
+ enable-auto-replace enable auto replace
+ disable-auto-replace disable auto replace
+</pre>
+ <h3>status</h3>
+ <p>Managerが持っているハッシュ空間を取得して表示する。</p>
+ <pre>$ kumoctl localhost status
+hash space timestamp:
+ Wed Dec 03 22:15:45 +0900 2008 clock 58
+attached node:
+ 127.0.0.1:8000 (active)
+ 127.0.0.1:8001 (fault)
+not attached node:
+ 127.0.0.1:8002
+</pre>
+ <p class="title">statusの実行例</p>
+ <p>
+ <strong>attached node</strong>はハッシュ空間に入っているServerの一覧を示している。<strong>(active)</strong>は正常動作中のServerで、<strong>(fault)</strong>はfaultフラグが立っているServerを示している。</p>
+ <p>
+ <strong>not attached node</strong>はハッシュ空間に入っていないか、入っているが(fault)状態でまだ再attachされていないServerの一覧を示している。</p>
+ <p>レプリケーションの再配置を行ったとき、Managerが2台起動していれば2つのManager間で新しいハッシュ空間が同期される。ただし新しいハッシュ空間が空の時は同期されない。</p>
+ <p>この理由は、障害が発生していたManagerを復旧したときに空のハッシュ空間が同期されてしまう可能性があるため。起動した直後はクロック(後述)が調整されていないために、ハッシュ空間の新旧の比較が正常に機能しない。このため空のハッシュ空間を受け取ったときは無視するようになっている。 # FIXME この動作は正しい?もっと良い回避方法は無いか?</p>
+ <br />
+ <h3>attach</h3>
+ <p>statusで<strong>not attached node</strong>に表示されているServerをハッシュ空間に組み入れ、レプリケーションの再配置を開始する。</p>
+ <h3>attach-noreplace</h3>
+ <p>attachと同じだがレプリケーションの再配置を開始しない。ただし再配置をしないまま長い間放置してはいけない。</p>
+ <p>再配置を行わないと、エラーが積もってGatewayから最新のハッシュ空間を要求されたとき(後述)、Gatewayが持っているハッシュ空間とServerが持っているハッシュ空間が食い違ってしまう。食い違うとsetやdeleteがいつまで経っても成功しなくなってしまう。</p>
+ <h3>detach</h3>
+ <p>statusで<strong>attached node</strong>に表示されていて(fault)状態のServerをハッシュ空間から取り除き、レプリケーションの再配置を開始する。</p>
+ <h3>detach-noreplace</h3>
+ <p>detachと同じだがレプリケーションの再配置を開始しない。再配置をしないまま長い間放置してはいけない。</p>
+ <h3>replace</h3>
+ <p>レプリケーションの再配置を開始する。</p>
+ <h3>backup</h3>
+ <p>コールドバックアップを作成する。バックアップはServerで作成され、元のデータベース名にsuffixを付けた名前のファイルにデータベースがコピーされる。手元にバックアップを持ってくるには、rsyncやscpなどを使ってServerからダウンロードする。</p>
+ <p>suffixは省略するとその日の日付(YYMMDD)が使われる。</p>
+ <p>作成されたバックアップファイルは、kumomergedbコマンドを使って1つのファイルに結合することができる。</p>
+ <pre class="code_sh">$ kumomergedb backup.tch-20090101 \
+ server1.tch-20090101 server2.tch-20090101 server3.tch-20090101
+</pre>
+ <p class="title">kumomergedbコマンドの実行例</p>
+ <br />
+ <h2>kumostat</h2>
+ <p>kumostatコマンドを使うとServerの状態を取得することができる。</p>
+ <p>Rubyで書かれたスクリプト。実行するにはgemでmsgpackパッケージをインストールする。</p>
+ <p>第1引数にServerのホスト名とポート番号を指定し、第2引数にコマンドを指定する。</p>
+ <pre>Usage: kumostat address[:port=19800] command [options]
+command:
+ pid get pid of server process
+ uptime get uptime
+ time get UNIX time
+ version get version
+ cmd_get get number of get requests
+ cmd_set get number of set requests
+ cmd_delete get number of delete requests
+ items get number of stored items
+</pre>
+ <h3>pid</h3>
+ <p>kumo-serverプロセスのpidを取得する</p>
+ <h3>uptime</h3>
+ <p>kumo-serverプロセスの起動時間を取得する。単位は秒。</p>
+ <h3>time</h3>
+ <p>kumo-serverプロセスが走っているホストのUNIXタイムを取得する。</p>
+ <h3>version</h3>
+ <p>バージョンを取得する。</p>
+ <h3>cmd_get / cmd_set / cmd_delete</h3>
+ <p>それぞれGatewayからのGetリクエスト、Setリクエスト、Deleteリクエストを処理した回数を取得する。</p>
+ <h3>items</h3>
+ <p>データベースに入っているエントリの数を取得する。</p>
+ <br />
+ <br />
+ <h2>ログ</h2>
+ <p>kumo-manager, kumo-server, kumo-gatewayは、それぞれ2種類のログを出力する:</p>
+ <dl>
+ <dt>テキストログ</dt>
+ <dd>行区切りのテキストフォーマットのログ。通常標準出力に出力される</dd>
+ <dt>バイナリログ</dt>
+ <dd>MessagePackでシリアライズされたログ</dd>
+ </dl>
+ <p>テキストログは常に出力される。<strong>-v</strong>オプションを付けると冗長なログも出力されるようになる。テキストログはファイルに書き出すこともできるが、ログローテーションはサポートしていない。<strong>-d &lt;path.pid&gt;</strong>オプションを指定してデーモンとして起動するか、<strong>-o -</strong>オプションを指定すると、ログに色が付かなくなる。</p>
+ <p>バイナリログは<strong>-g &lt;path.mpac&gt;</strong>オプションを付けたときだけ出力される。<strong>-v</strong>オプションは影響しない。バイナリログはSIGHUPシグナルを受け取るとログファイルを開き直すため、logrotateなどを使ってログローテーションができる。</p>
+ <p>バイナリログは<strong>kumolog</strong>コマンドを使ってテキストに変換して読むことができる。</p>
+ <pre class="code_sh">$ kumolog manager.mpac
+</pre>
+ <p class="title">kumologコマンドの実行例</p>
+ <br />
+ <h2>チューニング</h2>
+ <h3>データベースのチューニング</h3>
+ <p>Tokyo Cabinetのチューニングによって性能が大きく変わる。kumo-serverを起動する前にあらかじめ<strong>tchmgr</strong>コマンドでデータベースファイルを作成しておく。</p>
+ <p>チューニングのパラメータはTokyo Cabinetのドキュメント参照。<a href="http://tokyocabinet.sourceforge.net/spex-ja.html">http://tokyocabinet.sourceforge.net/spex-ja.html</a>
+ </p>
+ <pre class="code_sh">$ tchmgr create /path/to/database.tch 1048568 # バケット数を2097136個にして作成
+$ kumo-server -m localhost -s /path/to/database.tch
+</pre>
+ <h3>タイムアウト時間とkeepalive間隔の調整</h3>
+ <p># TODO</p>
+ <br />
+ <h2>死活監視と再配置</h2>
+ <h3>障害の検出</h3>
+ <p>ManagerとServerの接続では、あるノードにリクエストまたはレスポンスを送信しようとしたときに、そのノードとのコネクションが一本も存在せず、さらにconnect(2)が4回<sup class="footnote">
+ <a title="--connect-retry-limitで指定" href="#footnote4-2" id="footnote4-2-anchor">*2</a>
+ </sup>連続して失敗したら、そのノードはダウンしたと見なす。</p>
+ <p>ManagerとServerは2秒間隔<sup class="footnote">
+ <a title="--keep-alive-interval引数で指定" href="#footnote4-3" id="footnote4-3-anchor">*3</a>
+ </sup>でkeepaliveメッセージをやりとりしているので、いつも何らかのリクエストかレスポンスを送ろうとしている状態になっている。</p>
+ <p>connect(2)は次の条件で失敗する:</p>
+ <ul class="ul1">
+ <li>接続相手から明示的に接続を拒否された(Connection Refused)</li>
+ <li>接続相手からの応答がない時間が3ステップ<sup class="footnote">
+ <a title="--connect-timeout-steps引数で指定" href="#footnote4-4" id="footnote4-4-anchor">*4</a>
+ </sup>続いた。1ステップは0.5秒<sup class="footnote">
+ <a title="--clock-interval引数で指定" href="#footnote4-5" id="footnote4-5-anchor">*5</a>
+ </sup>
+ </li>
+ </ul>
+ <br />
+ <h3>接続の検出</h3>
+ <p>ManagerとServerの接続では、あるノードから接続を受け付けた後、そのノードから初期ネゴシエーションメッセージを受け取り、かつそのメッセージのフォーマットが正しければ、そのノードが新たに起動したと見なす。</p>
+ <br />
+ <h3>ハッシュ空間の更新</h3>
+ <p>Consistent Hashingのハッシュ空間を更新できるのはManagerだけで、最新のハッシュ空間は常にManagerが持っている。</p>
+ <p>通常動作時には1種類のハッシュ空間しか存在しないが、レプリケーションの再配置を行っている間は2種類のバージョンが存在する。最新のもの(Serverの追加/切り離しの更新が反映されている)は<strong>whs</strong>、1つ前のバージョン(Serverの追加/切り離しの更新が反映されていない)は<strong>rhs</strong>という名前が付いている。</p>
+ <br />
+ <p>Managerはkumoctlコマンドでレプリケーションの再配置を行うように指令されると、まずServerの追加/切り離しをwhsに反映する。もう1台のManagerが存在すればそのManagerに更新したwhsを送信する。</p>
+ <p>次に認識しているすべてのServerにwhsを送信し、レプリケーションのコピーを行うようにコマンドを送る。Serverは自分が持っているwhsとManagerから送られてきたwhsを比較し、必要なら他のServerにデータのコピーを行う(このときデータベースを上から下まで読み込む)。Serverはコピーが終わったらwhsをrhsにコピーする。</p>
+ <p>Serverはすべてのデータを確認し終えたら、Managerにコピーが終了した旨を通知する。ManagerはすべてのServerでコピーが終了した通知を受け取ったら、whsをrhsにコピーする。また、認識しているすべてのサーバーにレプリケーションの削除を行うようにコマンドを送る。Serverはwhsを参照して、自分が持っている必要がないデータがデータベースの中に入っていたら、それを削除する(このときもデータベースを上から下まで読み込む)。</p>
+ <p>Managerはレプリケーションのコピーを行っている最中にServerがダウンしたことを検知したら、すべてのServerからレプリケーションのコピーが終了した通知を受け取っても、レプリケーションの削除を行わない。</p>
+ <p>ServerはGatewayからget/set/deleteリクエストを受け取ったとき、そのkeyに対する割り当てノードが本当に自分であるか確認するために、getの場合はrhsを、set/deleteの場合はwhsを参照する。</p>
+ <br />
+ <h3>レプリケーションの再配置アルゴリズム</h3>
+ <p># TODO レプリケーションの再配置アルゴリズム</p>
+ <p>logic/srv_replace.cc:Server::replace_copy()</p>
+ <br />
+ <h2>レプリケーション</h2>
+ <h3>set/deleteの伝播</h3>
+ <p>Gatewayにsetリクエストを送信すると、keyにハッシュ関数を適用してハッシュ空間から検索し、一番最初にヒットしたServerに対してsetリクエストが送信される。</p>
+ <p>setリクエストを受け取ったServerは、keyのハッシュをハッシュ空間から検索し、自分が確かに最初にヒットするServerかどうか確かめる。そうでなければGatewayに「ハッシュ空間が古いぞ」とエラーを返す。</p>
+ <p>次にServerは、自分の次のServerと次の次のServerにデータをコピーする。このときコピー先のServerにfaultフラグが立っていたら、そのServerにはコピーしない。</p>
+ <p>Gatewayはset/deleteが何回失敗しても、次のServerにフォールバックすることはない。set先のServerが別のServerに切り替わるのは、Managerから新しいハッシュ空間を届いたときのみ。</p>
+ <p>以上の仕組みから、あるkeyをset/deleteするときは必ず単一のServerを経由することになる。このためほぼ同時にset/deleteされても必ず順序が付けられ、常に最新の結果がだけが残る。</p>
+ <br />
+ <h3>getのフォールバック</h3>
+ <p>Gatewayはgetリクエストがタイムアウトしたり失敗したりすると、ハッシュ空間上の次のServerにリクエストする。それでもタイムアウトしたときは次の次のServerにリクエストする。リトライ回数の上限に達するまで、最初のServer→次のServer→次の次のServer→最初のServer→…とリトライが繰り返される。</p>
+ <p>getはManagerから新しいハッシュ空間が届くのを待つことなくフォールバックする。</p>
+ <br />
+ <h3>タイムアウト</h3>
+ <p>GatewayでもServerでもManagerでも、リクエストを送ってから10ステップ(1ステップは0.5秒<sup class="footnote">
+ <a title="--clock-interval引数で指定" href="#footnote4-6" id="footnote4-6-anchor">*6</a>
+ </sup>)の間にレスポンスが返ってこないと、そのリクエストはタイムアウトしてエラーになる。</p>
+ <p>プログラムから見てTCPコネクションが確立しているか否かはタイムアウトには関係しない。コネクションが確立していなくても時間以内に再接続してレスポンスが返れば正常通り処理が続行され、コネクションが確立していても時間以内にレスポンスが返ってこなければタイムアウトする。</p>
+ <p>GatewayはServerに送ったリクエストがエラーになった回数が5回<sup class="footnote">
+ <a title="--renew-threashold引数で指定" href="#footnote4-7" id="footnote4-7-anchor">*7</a>
+ </sup>以上失敗すると、Managerから最新のハッシュ空間を取得する。</p>
+ <br />
+ <h3>リトライ</h3>
+ <p>Gatewayはsetは最大20回<sup class="footnote">
+ <a title="--set-retry引数で指定" href="#footnote4-8" id="footnote4-8-anchor">*8</a>
+ </sup>まで、deleteは最大20回<sup class="footnote">
+ <a title="--delete-retry引数で指定" href="#footnote4-9" id="footnote4-9-anchor">*9</a>
+ </sup>まで、getは最大5×(レプリケーション数==3 + 1)回<sup class="footnote">
+ <a title="係数は--get-retry引数で指定" href="#footnote4-10" id="footnote4-10-anchor">*10</a>
+ </sup>までリトライする。制限回数までリトライしても失敗したらアプリケーションにエラーが返される。</p>
+ <div class="footnote_view">
+ <p>
+ <a href="#footnote4-1-anchor" id="footnote4-1">*1</a>ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台</p>
+ <p>
+ <a href="#footnote4-2-anchor" id="footnote4-2">*2</a>--connect-retry-limitで指定</p>
+ <p>
+ <a href="#footnote4-3-anchor" id="footnote4-3">*3</a>--keep-alive-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-4-anchor" id="footnote4-4">*4</a>--connect-timeout-steps引数で指定</p>
+ <p>
+ <a href="#footnote4-5-anchor" id="footnote4-5">*5</a>--clock-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-6-anchor" id="footnote4-6">*6</a>--clock-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-7-anchor" id="footnote4-7">*7</a>--renew-threashold引数で指定</p>
+ <p>
+ <a href="#footnote4-8-anchor" id="footnote4-8">*8</a>--set-retry引数で指定</p>
+ <p>
+ <a href="#footnote4-9-anchor" id="footnote4-9">*9</a>--delete-retry引数で指定</p>
+ <p>
+ <a href="#footnote4-10-anchor" id="footnote4-10">*10</a>係数は--get-retry引数で指定</p>
+ </div>
+ <br />
+ <h2>クロック</h2>
+ <p>データベースに保存されているすべてのvalueや、ハッシュ空間には、クロック(=タイムスタンプ)が付与されている。value同士やハッシュ空間同士でどちらが新しいかを比べるために利用している。</p>
+ <p>ref:<a href="http://funini.com/kei/logos/clock.shtml">Lamport Clockの解説</a>
+ </p>
+ <h3>クロックのフォーマット</h3>
+ <p>クロックは64ビットの整数で、上位32ビットにはUNIXタイム(精度は秒)、下位32ビットにはLamport Clockが入っている。</p>
+ <p>UNIXタイムが上位に入っているので、Server/Manager同士の時刻が1秒以上ずれていると、Lamport Clockに関係なく間違った比較が行われてしまう。</p>
+ <h3>データベースのフォーマット</h3>
+ <p>データベースにkeyを保存するとき、先頭の64ビットにkeyのハッシュを負荷して保存する。</p>
+ <p>データベースにvalueを保存するとき、先頭の64ビットにクロックを付加して保存する。またその次の64ビットも予約してあるが、使っていない。</p>
+ <pre>Database entry format
+Big endian
+
+key:
++--------+-----------------+
+| 64 | ... |
++--------+-----------------+
+hash
+ key
+
+value:
++--------+--------+-----------------+
+| 64 | 64 | ... |
++--------+--------+-----------------+
+clocktime
+ meta
+ data
+</pre>
+ <h3>レプリケーションでの利用</h3>
+ <p>Serverから別のServerにデータをコピーするとき、後から来たsetリクエストのレプリケーションが、先に来たsetリクエストのレプリケーションを追い抜いて先行してしまうことが発生し得る。Serverはレプリケーションを受け取ったとき、既に保存されているvalueのクロックと新たに届いたvalueのクロックを比べ、新たに届いた方が新しかった場合のみデータベースを更新する。</p>
+ <p>レプリケーションの再配置を行うとき、ほとんどの場合はレプリケーションされたどのServerも同じデータを持っているが、setが失敗していた場合は異なるデータを持っている可能性がある。このときどのServerが持っているデータが最新なのか比べる必要があり、クロックを利用して比較する。</p>
+ <h3>Manager間の協調動作での利用</h3>
+ <p>Managerが2台動作しているとき、どちらが持っているハッシュ空間が最新なのかを比べる必要がある。ハッシュ空間を更新するときに更新した時のクロックを付与しておき、比較するときにこのクロックを利用する。</p>
+ <div class="footnote_view">
+ <p>
+ <a href="#footnote4-1-anchor" id="footnote4-1">*1</a>ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台</p>
+ <p>
+ <a href="#footnote4-2-anchor" id="footnote4-2">*2</a>--connect-retry-limitで指定</p>
+ <p>
+ <a href="#footnote4-3-anchor" id="footnote4-3">*3</a>--keep-alive-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-4-anchor" id="footnote4-4">*4</a>--connect-timeout-steps引数で指定</p>
+ <p>
+ <a href="#footnote4-5-anchor" id="footnote4-5">*5</a>--clock-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-6-anchor" id="footnote4-6">*6</a>--clock-interval引数で指定</p>
+ <p>
+ <a href="#footnote4-7-anchor" id="footnote4-7">*7</a>--renew-threashold引数で指定</p>
+ <p>
+ <a href="#footnote4-8-anchor" id="footnote4-8">*8</a>--set-retry引数で指定</p>
+ <p>
+ <a href="#footnote4-9-anchor" id="footnote4-9">*9</a>--delete-retry引数で指定</p>
+ <p>
+ <a href="#footnote4-10-anchor" id="footnote4-10">*10</a>係数は--get-retry引数で指定</p>
+ </div>
+ </body>
+</html>
View
BIN doc/memo.pdf
Binary file not shown.
View
401 doc/memo.txt
@@ -0,0 +1,401 @@
+?title Kumofs memo -- Kumo Fast Storage rev.948
+?author FURUHASHI Sadayuki
+
+
+*概要
+kumofsはkey-value型のデータを保存する分散ストレージ。key=>valueを保存する''set''、keyを取得する''get''、keyを削除する''delete''の3つの操作をサポートする。
+データを保持する''Server''、Server群を管理する''Manager''、アプリケーションからのリクエストをServerに中継する''Gateway''の3種類のノードでシステムを構成する。
+データは複数のServerに分散して保存されるため、Serverを追加するほど性能が向上する。
+データは3台のServerにコピーされて保存される。2台までならServerがダウンしても動作し続ける。
+Server群はManagerによって死活監視されている。Serverがダウンしたら、そのServerは直ちにシステムから切り離される。ただし1台か2台のManagerが起動していないとServerの切り離しが行われないので、Managerが1台も起動していない状態でServerがダウンするとシステムが停止してしまう。
+Serverを追加したり切り離したりした後、その状態をシステムに反映するには、レプリケーションされたデータの再配置を行う必要がある。これは自動では行われず、''kumoctlコマンド''を使って手動で行う。
+
+
+**Consistent Hashing
+Consistent Hashingを使ってデータを分散して保存する((ハッシュ関数はSHA-1で、下位64ビットのみ使う。仮想ノードは128台))。
+Serverがダウンしたときは、そのServerの仮想ノードに''faultフラグ''がセットされる。set/get/deleteはfaultフラグがセットされたServerをスキップして行われる。つまり、通常動作時はレプリケーションは3つ作成されるが、1台がfault状態ならコピーは2つ、2台がfault状態ならコピーは1つしか作成されないkeyが存在することになる。fault状態のServerが3台以上になると、get/set/deleteが失敗し続けるkeyが存在することになる。
+Serverがダウンしてもfaultフラグがセットされるだけで、レプリケーションの再配置は行われない。faultフラグがセットされたServerが存在する状態で、kumoctlコマンドを使って''detach''コマンドをManagerに送信すると、faultフラグがセットされたServerがハッシュ空間から取り除かれる。同時にレプリケーションの再配置が行われ、すべてのkeyに対してレプリケーションが3つ作成されるようにデータがコピーされる。
+Serverが追加されてもすぐにはハッシュ空間には追加されず、レプリケーションの再配置は行われない。新たなServerが起動している状態で、kumoctlコマンドを使って''attach''コマンドをManagerに送信すると、新しいServerがハッシュ空間に追加される。同時にレプリケーションの再配置が行われ、すべてのkeyに対してレプリケーションが3つだけ存在するようにデータが移動される。
+
+# TODO: auto-replace
+
+**set/get/deleteの保証範囲
+***set(key, value)
+key=>valueを保存する。保存できれば成功を返す。保存できなければエラーを返す。
+既にkeyが保存されていたとき、setが成功した場合はkeyの値は確実に上書きされている。
+setが失敗したとき、keyの値は不定になっている。これは失敗したときにロールバックを行わないため。ロールバックを一貫性を損なうことなく行うための高級なアルゴリズムは実装されていない/使うと性能が低下してしまう。
+Serverはレプリケーション先の2台〜0台のすべてのServerにデータが受信されたことを確認してからGatewayにレスポンスを返す。どれか1台でもコピー処理が失敗したらエラーを返す。つまり、アプリケーションに成功が返されたときはfault状態でないすべてのServerにレプリケーションがコピーされており、それ以降に古いデータが読み出されることはない。ただしディスクに書き込まれているとは限らない。
+
+***get(key)
+keyをsetするリクエストが成功していた場合は、そのkeyに対応するvalueを返す。setが失敗していた場合は、nullか、setに失敗したvalueが返る。それ以外であればnullを返す。
+keyをsetするリクエストが成功してvalueが保存されていたとしても、レプリケーションされたすべてのServerの負荷が非常に高いために応答できない場合は、getがタイムアウトする可能性がある。
+keyが保存されていなかった場合はエラーにならないが、タイムアウトした場合はエラーになる。
+
+***delete(key)
+keyを削除する。
+再配置処理を行っている間にdeleteを行うと、削除されないことがある。また同じkeyに対してdeleteとsetをほぼ同時に行うと、削除されないことがある。これはレプリケーションを行うServer同士のやりとりが、Gatewayが送出したdeleteリクエストと入れ違う可能性があるため。
+Serverの引数を調整することで、deleteが一貫性を保たない確率を減らすことができる。
+
+
+**動作環境と制限
+***サーバーの時刻設定
+ManagerとServerを動作させるホストの時刻設定は、TIME_ERROR_MARGIN秒(コンパイル時に決定。デフォルトでは5秒)以上ずれていると正常に動作しない。またUTCとlocaltimeはどちらかに揃える必要がある。
+
+# TODO
+
+
+@footnote
+
+
+*インストールと実行
+**依存関係
+***動作環境
+-linux >= 2.6.18
+-glibc >= XXX
+
+***コンパイル時に必要なもの
+-g++ >= 4.1
+-ragel >= 6.3
+-git >= XXX
+
+***コンパイル時と実行時に必要なもの
+-ruby >= 1.8
+-rubygems
+-libcrypto(openssl)
+-zlib >= XXX
+-Tokyo Cabinet >= 1.4.9
+
+**コンパイル
+まず最新のMessagePackをインストールする。
+>|sh|
+$ git clone git://git.sourceforge.jp/gitroot/msgpack/msgpack.git
+$ cd msgpack
+$ ./bootstrap && ./configure && make
+$ sudo make install
+$ cd ruby
+$ ./gengem
+$ sudo gem install gem/pkg/msgpack-*.gem
+||<
+^title MessagePackのインストール
+
+次にkumofsをインストールする。
+>|sh|
+$ ./configure && make && make install
+||<
+
+以下の4つのコマンドがインストールされる:
+:kumo-manager:Managerノード。Serverノードの管理をする。
+:kumo-server:Serverノード。実際にデータを保存する。
+:kumo-gateway:Gatewayノード。memcachedプロトコルのサーバーで、アプリケーションからの要求をServerノードに中継する。
+:kumoctl:Managerノードを制御するための管理コマンド
+:kumolog:バイナリフォーマットのログをテキストフォーマットに変換する
+:kumomergedb:コールドバックアップファイルをマージする
+
+***configureフラグ
+:--with-msgpack=DIR:MessagePackがインストールされているディレクトリを指定する
+:--with-tokyocabinet=DIR:Tokyo Cabinetがインストールされているディレクトリを指定する
+:--enable-trace:画面を埋め尽くすほど冗長なデバッグ用のメッセージを出力するようにする
+:--with-tcmalloc[=DIR]:tcmallocとリンクする
+
+**実行例
+***Manager 2台, Server 4台を使った冗長構成
+''s1''〜''s4''の4台でクラスタを構成し、''c1''と''c2''で動作するアプリケーションから利用する例。
+s1〜s4でServerを起動し、''s1''と''s2''では同時にManagerも起動する。''c1''と''c2''ではGatewayを起動する。
+>|sh|
+[s1]$ kumo-manager -v -l s1 -p s2 # Manager同士は互いに指定する
+[s2]$ kumo-manager -v -l s2 -p s1 # Manager同士は互いに指定する
+[s1]$ kumo-server -v -m s1 -p s2 -l s1 -s database.tch # -mと-pでManagerを指定する
+[s2]$ kumo-server -v -m s1 -p s2 -l s2 -s database.tch # -lは常に自ホストのアドレス
+[s3]$ kumo-server -v -m s1 -p s2 -l s3 -s database.tch # -sはデータベース名
+[s4]$ kumo-server -v -m s1 -p s2 -l s4 -s database.tch # -vは冗長なメッセージを出力
+[c1]$ kumo-gateway -v -m s1 -p s2 -t 11211 # 11211/tcpでmemcachedテキストプロトコル
+[c2]$ kumo-gateway -v -m s1 -p s2 -t 11211 # を待ち受ける
+||<
+
+***localhostでクラスタを構成する
+localhostでManagerノード1台、Server 2台を使ってクラスタを構成する例。
+>|sh|
+[localhost]$ kumo-manager -v -l localhost # Managerを1台で運用するときは-pを省略
+ # kumo-serverはポートを変えて起動する
+[localhost]$ kumo-server -v -m localhost -l localhost:19801 -L 19901 -s database1.tch
+[localhost]$ kumo-server -v -m localhost -l localhost:19802 -L 19902 -s database2.tch
+[localhost]$ kumo-gateway -v -m localhost -t 11211
+||<
+
+
+**主な引数
+**共通
+:-o <path.log>:ログを標準出力ではなく指定されたファイルに出力する
+:-g <path.mpac>:バイナリログを指定されたファイルに出力する
+:-d <path.pid>:デーモンになる。指定されたファイルにpidを書き出す
+:-v:WARNよりレベルの低いメッセージを出力する
+:-Ci <sec>:タイマークロックの間隔を秒で指定する。小数を指定できる
+:-Ys <sec>:connect(2)のタイムアウト時間を秒で指定する。小数を指定できる
+:-Yn <num>:connect(2)のリトライ回数を指定する
+:-TR <num>:送信用スレッドの数を指定する
+:-TW <num>:受信用スレッドの数を指定する
+
+***kumo-manager
+:-l <address>:待ち受けるアドレス。''他のノードから見て''接続できるホスト名とポート番号を指定する
+:-p <address>:もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する
+:-c <port>:kumoctlからのコマンドを受け付けるポート番号を指定する
+:--auto-replace:Serverが追加・切断されたときに、マニュアル操作を待たずにレプリケーションの再配置を自動的に行うようにする。実行中でもkumoctlコマンドを使って変更できる
+
+***kumo-server
+:-l <address>:待ち受けるアドレス。''他のノードから見て''接続できるホスト名とポート番号を指定する
+:-L <port>:kumo-serverが待ち受けるもう一つのポートのポート番号を指定する
+:-m <address>:kumo-managerのホスト名とポート番号を指定する
+:-p <address>:もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する
+:-s <path.tch>:データを保存するデータベースファイルのパスを指定する
+:-f <dir>:レプリケーションの再配置に使う一時ファイルを保存するディレクトリを指定する。データベースファイルのサイズに応じて十分な空き容量が必要
+:-gS <seconds>:deleteしたエントリのクロックを保持しておくメモリ使用量の上限をKB単位で指定する
+:-gN <seconds>:deleteしたエントリのクロックを保持しておく最小時間を指定する。メモリ使用量が上限に達していると、最大時間に満たなくても最小時間を過ぎていれば削除される。
+:-gX <seconds>:deleteしたエントリのクロックを保持しておく最大時間を指定する
+
+***kumo-gateway
+:-m <address>:kumo-managerのホスト名とポート番号を指定する
+:-p <address>:もし存在するなら、もう一台のkumo-managerのホスト名とポート番号を指定する
+:-t <port>:memcachedテキストプロトコルを待ち受けるポート番号を指定する
+:-G <number>:getの最大リトライ回数を指定する
+:-S <number>:setの最大リトライ回数を指定する
+:-D <number>:deleteの最大リトライ回数を指定する
+:-As:set操作でレプリケーションするとき、レプリケーション完了の応答を待たずに成功を返すようにする
+:-Ad:delete操作でレプリケーションするとき、レプリケーション完了の応答を待たずに成功を返すようにする
+
+@footnote
+
+
+*kumoctl
+kumoctlコマンドを使うとManagerの状態を取得したり、コマンドを送ったりできる。
+Rubyで書かれたスクリプト。実行するにはgemでmsgpackパッケージをインストールする。
+第1引数にManagerのホスト名とポート番号を指定し、第2引数にコマンドを指定する。
+>|sh|
+$ kumoctl --help
+Usage: kumoctl address[:port=19799] command [options]
+command:
+ status get status
+ attach attach all new servers and start replace
+ attach-noreplace attach all new servers
+ detach detach all fault servers and start replace
+ detach-noreplace detach all fault servers
+ replace start replace without attach/detach
+ backup [suffix=????????] create backup with specified suffix
+ enable-auto-replace enable auto replace
+ disable-auto-replace disable auto replace
+||<
+
+**status
+Managerが持っているハッシュ空間を取得して表示する。
+>||
+$ kumoctl localhost status
+hash space timestamp:
+ Wed Dec 03 22:15:45 +0900 2008 clock 58
+attached node:
+ 127.0.0.1:8000 (active)
+ 127.0.0.1:8001 (fault)
+not attached node:
+ 127.0.0.1:8002
+||<
+^title statusの実行例
+''attached node''はハッシュ空間に入っているServerの一覧を示している。''(active)''は正常動作中のServerで、''(fault)''はfaultフラグが立っているServerを示している。
+''not attached node''はハッシュ空間に入っていないか、入っているが(fault)状態でまだ再attachされていないServerの一覧を示している。
+
+レプリケーションの再配置を行ったとき、Managerが2台起動していれば2つのManager間で新しいハッシュ空間が同期される。ただし新しいハッシュ空間が空の時は同期されない。
+この理由は、障害が発生していたManagerを復旧したときに空のハッシュ空間が同期されてしまう可能性があるため。起動した直後はクロック(後述)が調整されていないために、ハッシュ空間の新旧の比較が正常に機能しない。このため空のハッシュ空間を受け取ったときは無視するようになっている。 # FIXME この動作は正しい?もっと良い回避方法は無いか?
+
+
+**attach
+statusで''not attached node''に表示されているServerをハッシュ空間に組み入れ、レプリケーションの再配置を開始する。
+
+**attach-noreplace
+attachと同じだがレプリケーションの再配置を開始しない。ただし再配置をしないまま長い間放置してはいけない。
+再配置を行わないと、エラーが積もってGatewayから最新のハッシュ空間を要求されたとき(後述)、Gatewayが持っているハッシュ空間とServerが持っているハッシュ空間が食い違ってしまう。食い違うとsetやdeleteがいつまで経っても成功しなくなってしまう。
+
+**detach
+statusで''attached node''に表示されていて(fault)状態のServerをハッシュ空間から取り除き、レプリケーションの再配置を開始する。
+
+**detach-noreplace
+detachと同じだがレプリケーションの再配置を開始しない。再配置をしないまま長い間放置してはいけない。
+
+**replace
+レプリケーションの再配置を開始する。
+
+**backup
+コールドバックアップを作成する。バックアップはServerで作成され、元のデータベース名にsuffixを付けた名前のファイルにデータベースがコピーされる。手元にバックアップを持ってくるには、rsyncやscpなどを使ってServerからダウンロードする。
+suffixは省略するとその日の日付(YYMMDD)が使われる。
+作成されたバックアップファイルは、kumomergedbコマンドを使って1つのファイルに結合することができる。
+>|sh|
+$ kumomergedb backup.tch-20090101 \
+ server1.tch-20090101 server2.tch-20090101 server3.tch-20090101
+||<
+^title kumomergedbコマンドの実行例
+
+
+*kumostat
+kumostatコマンドを使うとServerの状態を取得することができる。
+Rubyで書かれたスクリプト。実行するにはgemでmsgpackパッケージをインストールする。
+第1引数にServerのホスト名とポート番号を指定し、第2引数にコマンドを指定する。
+>||
+Usage: kumostat address[:port=19800] command [options]
+command:
+ pid get pid of server process
+ uptime get uptime
+ time get UNIX time
+ version get version
+ cmd_get get number of get requests
+ cmd_set get number of set requests
+ cmd_delete get number of delete requests
+ items get number of stored items
+||<
+
+**pid
+kumo-serverプロセスのpidを取得する
+
+**uptime
+kumo-serverプロセスの起動時間を取得する。単位は秒。
+
+**time
+kumo-serverプロセスが走っているホストのUNIXタイムを取得する。
+
+**version
+バージョンを取得する。
+
+**cmd_get / cmd_set / cmd_delete
+それぞれGatewayからのGetリクエスト、Setリクエスト、Deleteリクエストを処理した回数を取得する。
+
+**items
+データベースに入っているエントリの数を取得する。
+
+
+
+*ログ
+kumo-manager, kumo-server, kumo-gatewayは、それぞれ2種類のログを出力する:
+:テキストログ:行区切りのテキストフォーマットのログ。通常標準出力に出力される
+:バイナリログ:MessagePackでシリアライズされたログ
+
+テキストログは常に出力される。''-v''オプションを付けると冗長なログも出力されるようになる。テキストログはファイルに書き出すこともできるが、ログローテーションはサポートしていない。''-d <path.pid>''オプションを指定してデーモンとして起動するか、''-o -''オプションを指定すると、ログに色が付かなくなる。
+
+バイナリログは''-g <path.mpac>''オプションを付けたときだけ出力される。''-v''オプションは影響しない。バイナリログはSIGHUPシグナルを受け取るとログファイルを開き直すため、logrotateなどを使ってログローテーションができる。
+
+バイナリログは''kumolog''コマンドを使ってテキストに変換して読むことができる。
+>|sh|
+$ kumolog manager.mpac
+||<
+^title kumologコマンドの実行例
+
+
+*チューニング
+**データベースのチューニング
+Tokyo Cabinetのチューニングによって性能が大きく変わる。kumo-serverを起動する前にあらかじめ''tchmgr''コマンドでデータベースファイルを作成しておく。
+チューニングのパラメータはTokyo Cabinetのドキュメント参照。http://tokyocabinet.sourceforge.net/spex-ja.html
+>|sh|
+$ tchmgr create /path/to/database.tch 1048568 # バケット数を2097136個にして作成
+$ kumo-server -m localhost -s /path/to/database.tch
+||<
+
+**タイムアウト時間とkeepalive間隔の調整
+# TODO
+
+
+*死活監視と再配置
+**障害の検出
+ManagerとServerの接続では、あるノードにリクエストまたはレスポンスを送信しようとしたときに、そのノードとのコネクションが一本も存在せず、さらにconnect(2)が4回((--connect-retry-limitで指定))連続して失敗したら、そのノードはダウンしたと見なす。
+ManagerとServerは2秒間隔((--keep-alive-interval引数で指定))でkeepaliveメッセージをやりとりしているので、いつも何らかのリクエストかレスポンスを送ろうとしている状態になっている。
+connect(2)は次の条件で失敗する:
+-接続相手から明示的に接続を拒否された(Connection Refused)
+-接続相手からの応答がない時間が3ステップ((--connect-timeout-steps引数で指定))続いた。1ステップは0.5秒((--clock-interval引数で指定))
+
+
+**接続の検出
+ManagerとServerの接続では、あるノードから接続を受け付けた後、そのノードから初期ネゴシエーションメッセージを受け取り、かつそのメッセージのフォーマットが正しければ、そのノードが新たに起動したと見なす。
+
+
+**ハッシュ空間の更新
+Consistent Hashingのハッシュ空間を更新できるのはManagerだけで、最新のハッシュ空間は常にManagerが持っている。
+通常動作時には1種類のハッシュ空間しか存在しないが、レプリケーションの再配置を行っている間は2種類のバージョンが存在する。最新のもの(Serverの追加/切り離しの更新が反映されている)は''whs''、1つ前のバージョン(Serverの追加/切り離しの更新が反映されていない)は''rhs''という名前が付いている。
+
+
+Managerはkumoctlコマンドでレプリケーションの再配置を行うように指令されると、まずServerの追加/切り離しをwhsに反映する。もう1台のManagerが存在すればそのManagerに更新したwhsを送信する。
+次に認識しているすべてのServerにwhsを送信し、レプリケーションのコピーを行うようにコマンドを送る。Serverは自分が持っているwhsとManagerから送られてきたwhsを比較し、必要なら他のServerにデータのコピーを行う(このときデータベースを上から下まで読み込む)。Serverはコピーが終わったらwhsをrhsにコピーする。
+Serverはすべてのデータを確認し終えたら、Managerにコピーが終了した旨を通知する。ManagerはすべてのServerでコピーが終了した通知を受け取ったら、whsをrhsにコピーする。また、認識しているすべてのサーバーにレプリケーションの削除を行うようにコマンドを送る。Serverはwhsを参照して、自分が持っている必要がないデータがデータベースの中に入っていたら、それを削除する(このときもデータベースを上から下まで読み込む)。
+
+Managerはレプリケーションのコピーを行っている最中にServerがダウンしたことを検知したら、すべてのServerからレプリケーションのコピーが終了した通知を受け取っても、レプリケーションの削除を行わない。
+
+ServerはGatewayからget/set/deleteリクエストを受け取ったとき、そのkeyに対する割り当てノードが本当に自分であるか確認するために、getの場合はrhsを、set/deleteの場合はwhsを参照する。
+
+
+**レプリケーションの再配置アルゴリズム
+# TODO レプリケーションの再配置アルゴリズム
+logic/srv_replace.cc:Server::replace_copy()
+
+
+*レプリケーション
+**set/deleteの伝播
+Gatewayにsetリクエストを送信すると、keyにハッシュ関数を適用してハッシュ空間から検索し、一番最初にヒットしたServerに対してsetリクエストが送信される。
+setリクエストを受け取ったServerは、keyのハッシュをハッシュ空間から検索し、自分が確かに最初にヒットするServerかどうか確かめる。そうでなければGatewayに「ハッシュ空間が古いぞ」とエラーを返す。
+次にServerは、自分の次のServerと次の次のServerにデータをコピーする。このときコピー先のServerにfaultフラグが立っていたら、そのServerにはコピーしない。
+
+Gatewayはset/deleteが何回失敗しても、次のServerにフォールバックすることはない。set先のServerが別のServerに切り替わるのは、Managerから新しいハッシュ空間を届いたときのみ。
+
+以上の仕組みから、あるkeyをset/deleteするときは必ず単一のServerを経由することになる。このためほぼ同時にset/deleteされても必ず順序が付けられ、常に最新の結果がだけが残る。
+
+
+**getのフォールバック
+Gatewayはgetリクエストがタイムアウトしたり失敗したりすると、ハッシュ空間上の次のServerにリクエストする。それでもタイムアウトしたときは次の次のServerにリクエストする。リトライ回数の上限に達するまで、最初のServer→次のServer→次の次のServer→最初のServer→…とリトライが繰り返される。
+
+getはManagerから新しいハッシュ空間が届くのを待つことなくフォールバックする。
+
+
+**タイムアウト
+GatewayでもServerでもManagerでも、リクエストを送ってから10ステップ(1ステップは0.5秒((--clock-interval引数で指定)))の間にレスポンスが返ってこないと、そのリクエストはタイムアウトしてエラーになる。
+プログラムから見てTCPコネクションが確立しているか否かはタイムアウトには関係しない。コネクションが確立していなくても時間以内に再接続してレスポンスが返れば正常通り処理が続行され、コネクションが確立していても時間以内にレスポンスが返ってこなければタイムアウトする。
+
+GatewayはServerに送ったリクエストがエラーになった回数が5回((--renew-threashold引数で指定))以上失敗すると、Managerから最新のハッシュ空間を取得する。
+
+
+**リトライ
+Gatewayはsetは最大20回((--set-retry引数で指定))まで、deleteは最大20回((--delete-retry引数で指定))まで、getは最大5×(レプリケーション数==3 + 1)回((係数は--get-retry引数で指定))までリトライする。制限回数までリトライしても失敗したらアプリケーションにエラーが返される。
+
+@footnote
+
+
+*クロック
+データベースに保存されているすべてのvalueや、ハッシュ空間には、クロック(=タイムスタンプ)が付与されている。value同士やハッシュ空間同士でどちらが新しいかを比べるために利用している。
+ref:[[Lamport Clockの解説>http://funini.com/kei/logos/clock.shtml]]
+
+**クロックのフォーマット
+クロックは64ビットの整数で、上位32ビットにはUNIXタイム(精度は秒)、下位32ビットにはLamport Clockが入っている。
+UNIXタイムが上位に入っているので、Server/Manager同士の時刻が1秒以上ずれていると、Lamport Clockに関係なく間違った比較が行われてしまう。
+
+**データベースのフォーマット
+データベースにkeyを保存するとき、先頭の64ビットにkeyのハッシュを負荷して保存する。
+データベースにvalueを保存するとき、先頭の64ビットにクロックを付加して保存する。またその次の64ビットも予約してあるが、使っていない。
+>||
+Database entry format
+Big endian
+
+key:
++--------+-----------------+
+| 64 | ... |
++--------+-----------------+
+hash
+ key
+
+value:
++--------+--------+-----------------+
+| 64 | 64 | ... |
++--------+--------+-----------------+
+clocktime
+ meta
+ data
+||<
+
+**レプリケーションでの利用
+Serverから別のServerにデータをコピーするとき、後から来たsetリクエストのレプリケーションが、先に来たsetリクエストのレプリケーションを追い抜いて先行してしまうことが発生し得る。Serverはレプリケーションを受け取ったとき、既に保存されているvalueのクロックと新たに届いたvalueのクロックを比べ、新たに届いた方が新しかった場合のみデータベースを更新する。
+レプリケーションの再配置を行うとき、ほとんどの場合はレプリケーションされたどのServerも同じデータを持っているが、setが失敗していた場合は異なるデータを持っている可能性がある。このときどのServerが持っているデータが最新なのか比べる必要があり、クロックを利用して比較する。
+
+**Manager間の協調動作での利用
+Managerが2台動作しているとき、どちらが持っているハッシュ空間が最新なのかを比べる必要がある。ハッシュ空間を更新するときに更新した時のクロックを付与しておき、比較するときにこのクロックを利用する。
+
+@footnote
+
View
6 src/Makefile.am
@@ -0,0 +1,6 @@
+export ERB
+export RUBY
+export RAGEL
+
+SUBDIRS = mp mpsrc log kazuhiki rpc logic command
+
View
16 src/command/Makefile.am
@@ -0,0 +1,16 @@
+
+AM_CPPFLAGS = -I.. -I../logic
+AM_C_CPPFLAGS = -I.. -I../logic
+
+bin_PROGRAMS = kumomergedb
+bin_SCRIPTS = kumoctl kumostat kumolog
+
+EXTRA_DIST = $(bin_SCRIPTS)
+
+kumomergedb_SOURCES = \
+ mergedb.cc
+
+kumomergedb_LDADD = \
+ ../logic/libkumo_storage.a \
+ ../log/libkumo_log.a
+
View
285 src/command/kumoctl
@@ -0,0 +1,285 @@
+#!/usr/bin/env ruby
+
+begin
+ require 'rubygems'
+rescue LoadError
+end
+require 'msgpack'
+require 'socket'
+
+
+class KumoRPC
+ def initialize(host, port)
+ @sock = TCPSocket.open(host, port)
+ @pk = MessagePack::Unpacker.new
+ @buffer = ''
+ @nread = 0
+ @seqid = rand(1<<16) # FIXME 1 << 32
+ @callback = {}
+ end
+
+ private
+ def send_request(seq, cmd, param)
+ @sock.write [true, seq, cmd, param].to_msgpack
+ @sock.flush
+ rescue
+ @sock.close
+ raise
+ end
+
+ def receive_message
+ while true
+ if @buffer.length > @nread
+ @nread = @pk.execute(@buffer, @nread)
+ if @pk.finished?
+ msg = @pk.data
+ @pk.reset
+ @buffer.slice!(0, @nread)
+ @nread = 0
+ if msg[0]
+ process_request(msg[1], msg[2], msg[3])
+ else
+ process_response(msg[1], msg[3], msg[2])
+ end
+ return msg[1]
+ end
+ end
+ @buffer << @sock.sysread(1024)
+ end
+ end
+
+ def process_request(seqid, cmd, param)
+ raise "request received, excpect response"
+ end
+
+ def process_response(seqid, res, err)
+ if cb = @callback[seqid]
+ cb.call(res, err)
+ end
+ end
+
+ def synchronize_response(seqid)
+ while receive_message != seqid; end
+ end
+
+ def send_request_async(cmd, param, &callback)
+ seqid = @seqid
+ # FIXME 1 << 32
+ @seqid += 1; if @seqid >= 1<<16 then @seqid = 0 end
+ @callback[seqid] = callback if callback
+ send_request(seqid, cmd, param)
+ seqid
+ end
+
+ def send_request_sync(cmd, param)
+ res = nil
+ err = nil
+ seqid = send_request_async(cmd, param) {|rres, rerr|
+ res = rres
+ err = rerr
+ }
+ synchronize_response(seqid)
+ return [res, err]
+ end
+
+ def send_request_sync_ex(cmd, param)
+ res, err = send_request_sync(cmd, param)
+ raise "error #{err}" if err
+ res
+ end
+
+
+ def rpc_addr(raw)
+ if raw.length == 6
+ addr = Socket.pack_sockaddr_in(0, '0.0.0.0')
+ addr[2,6] = raw[0,6]
+ else
+ addr = Socket.pack_sockaddr_in(0, '::')
+ addr[2,2] = raw[0,2]
+ addr[8,20] = raw[2,20]
+ end
+ Socket.unpack_sockaddr_in(addr).reverse
+ end
+
+ public
+ def GetStatus
+ res = send_request_sync_ex(84, [])
+ form = {}
+ nodes = res[0]
+
+ clocktime = nodes.slice!(-1)
+ date = Time.at(clocktime >> 32)
+ clock = clocktime & ((1<<32)-1)
+
+ nodes.each {|nodes|
+ nodes.map! {|raw|
+ active = (raw.slice!(0) == "\1"[0])
+ rpc_addr(raw) << active
+ }
+ }
+
+ newcomers = res[1]
+ res[1].map! {|raw|
+ rpc_addr(raw)
+ }
+
+ return [nodes, newcomers, date, clock]
+ end
+
+ def AttachNewServers(replace)
+ send_request_sync_ex(85, [replace])
+ end
+
+ def DetachFaultServers(replace)
+ send_request_sync_ex(86, [replace])
+ end
+
+ def CreateBackup(suffix)
+ send_request_sync_ex(87, [suffix])
+ end
+
+ def SetAutoReplace(enable)
+ send_request_sync_ex(88, [enable])
+ end
+
+ def StartReplace()
+ send_request_sync_ex(89, [])
+ end
+
+ module Protocol
+ ControlGetStatus = 84
+ ControlAttachNewServers = 85
+ ControlDetachFaultServers = 86
+ ControlCreateBackup = 87
+ ControlSetAutoReplace = 88
+ ControlStartReplace = 89
+ GetStatus = 112
+ SetConfig = 112
+ end
+
+ CONTROL_DEFAULT_PORT = 19750
+ MANAGER_DEFAULT_PORT = 19700
+ SERVER_DEFAULT_PORT = 19800
+end
+
+if $0 == __FILE__
+
+
+
+class KumoManager < KumoRPC
+ def initialize(host, port)
+ super(host, port)
+ end
+
+ def AttachNewServers(replace)
+ send_request_sync_ex(Protocol::ControlAttachNewServers, [replace])
+ end
+
+ def DetachFaultServers(replace)
+ send_request_sync_ex(Protocol::ControlDetachFaultServers, [replace])
+ end
+
+ def CreateBackup(suffix)
+ send_request_sync_ex(Protocol::ControlCreateBackup, [suffix])
+ end
+
+ def SetAutoReplace(enable)
+ send_request_sync_ex(Protocol::ControlSetAutoReplace, [enable])
+ end
+
+ def StartReplace()
+ send_request_sync_ex(Protocol::ControlStartReplace, [])
+ end
+end
+
+
+$now = Time.now.strftime("%Y%m%d")
+
+def usage
+ puts "Usage: #{File.basename($0)} address[:port=#{KumoRPC::CONTROL_DEFAULT_PORT}] command [options]"
+ puts "command:"
+ puts " status get status"
+ puts " attach attach all new servers and start replace"
+ puts " attach-noreplace attach all new servers"
+ puts " detach detach all fault servers and start replace"
+ puts " detach-noreplace detach all fault servers"
+ puts " replace start replace without attach/detach"
+ puts " backup [suffix=#{$now }] create backup with specified suffix"
+ puts " enable-auto-replace enable auto replace"
+ puts " disable-auto-replace disable auto replace"
+ exit 1
+end
+
+if ARGV.length < 2
+ usage
+end
+
+addr = ARGV.shift
+host, port = addr.split(':', 2)
+port ||= KumoRPC::CONTROL_DEFAULT_PORT
+
+cmd = ARGV.shift
+
+case cmd
+when "stat", "status"
+ usage if ARGV.length != 0
+ attached, not_attached, date, clock =
+ KumoManager.new(host, port).GetStatus
+ puts "hash space timestamp:"
+ puts " #{date} clock #{clock}"
+ puts "attached node:"
+ attached.each {|addr, port, active|
+ puts " #{addr}:#{port} (#{active ? "active":"fault"})"
+ }
+ puts "not attached node:"
+ not_attached.each {|addr, port|
+ puts " #{addr}:#{port}"
+ }
+
+when "attach"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).AttachNewServers(true)
+
+when "attach-noreplace"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).AttachNewServers(false)
+
+when "detach"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).DetachFaultServers(true)
+
+when "detach-noreplace"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).DetachFaultServers(false)
+
+when "enable-auto-replace"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).SetAutoReplace(true)
+
+when "disable-auto-replace"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).SetAutoReplace(false)
+
+when "backup"
+ if ARGV.length == 0
+ suffix = $now
+ elsif ARGV.length == 1
+ suffix = ARGV.shift
+ else
+ usage
+ end
+ puts "suffix=#{suffix}"
+ p KumoManager.new(host, port).CreateBackup(suffix)
+
+when "replace"
+ usage if ARGV.length != 0
+ p KumoManager.new(host, port).StartReplace()
+
+else
+ puts "unknown command #{cmd}"
+ puts ""
+ usage
+end
+
+
+end # if $0 == __FILE__
View
212 src/command/kumolog
@@ -0,0 +1,212 @@
+#!/usr/bin/env ruby
+begin
+ require 'rubygems'
+rescue LoadError
+end
+require 'msgpack'
+require 'yaml'
+require 'pp'
+
+def fixstr(code)
+ r = ""
+ 8.times {|i|
+ c = ((code >> (8*(7-i))) & 0xff)
+ r << c.chr if c != 0
+ }
+ r
+end
+
+def do_recover(src, off)
+ puts "recover at #{off}"
+ sz = src.length
+ pk = MessagePack::Unpacker.new
+
+ while (sz - off) >= 4
+ br = src[off,4].unpack('N')[0]
+ doff = off + 4
+
+ failed = false
+ begin
+ #noff = pk.execute_limit(src, doff, doff+br)
+ noff = pk.execute(src, doff)
+ rescue
+ failed = true
+ end
+
+ if !failed && pk.finished? && noff - doff == br
+ return off
+ end
+
+ pk.reset
+ off += 1
+ end
+ return sz
+end
+
+def do_parse(src, count, &block)
+ off = 0
+ noff = 0
+ sz = src.length
+
+ pk = MessagePack::Unpacker.new
+
+ while true
+ return if (sz - off) < 4
+
+ br = src[off,4].unpack('N')[0]
+ off += 4
+
+ if (sz - off) < br
+ off = do_recover(src, off-3)
+ next
+ end
+
+ failed = false
+ begin
+ pk.reset
+ #noff = pk.execute_limit(src, off, off+br)
+ noff = pk.execute(src, off)
+ rescue
+ failed = true
+ end
+
+ if failed || !pk.finished? || noff - off != br
+ off = do_recover(src, off-3)
+ next
+ end
+
+ obj = pk.data
+
+ name = nil
+ version = 0
+ hash = {}
+ begin
+ unless obj.is_a?(Array) && obj.length == 3 &&
+ obj[0].is_a?(Numeric) && obj[1].is_a?(Numeric) &&
+ obj[2].is_a?(Hash)
+ off = do_recover(src, off-3)
+ next
+ end
+ name = fixstr(obj[0])
+ version = obj[1]
+ obj[2].each_pair {|k,v|
+ hash[fixstr(k)] = v
+ }
+ rescue
+ off = do_recover(src, off-3)
+ next
+ end
+
+ off += br
+
+ block.call(name, version, hash)
+ end
+end
+
+class Hash
+ def hmap(&block)
+ m = {}
+ each_pair {|k, v|
+ m[k] = block.call(k, v)
+ }
+ m
+ end
+end
+
+
+if ARGV.length == 0
+ puts "usage: #{File.basename($0)} <logfile.mpac>"
+ exit 1
+end
+
+
+conf = YAML.load DATA.read.gsub(/(^\t+)/) {
+ ' ' * $+.length
+}
+
+msgdb = conf["message"]
+
+filterdb = conf["filter"].hmap {|name, hash|
+ hash.hmap {|key, proc|
+ [ proc[0], eval("lambda{|val|#{proc[1]}}") ]
+ }
+}
+
+do_parse(File.read(ARGV[0]), 1<<30) {|name, version, hash|
+ msg = msgdb[name] || "#{name}.#{version}"
+
+ if filter = filterdb["#{name}.#{version}"]
+ filter.each_pair {|key, proc|
+ val = hash.delete(key)
+ hash[proc[0]] = proc[1].call(val)
+ }
+ end
+
+ vals = hash.map {|k, v|