Scripting Extension of Kyoto Tycoon.
This module is imported by the server program (ktserver) of Kyoto Tycoon. The server must be built enabling the scripting extension by Lua.
The start-up command has the option "-scr" to specify a script file defining functions in Lua.
$ ktserver -scr myscript.lua
You can define your own functions in the global name space so that clients can call them by specifying the name of each function. The function name must be composed of ASCII alphabets and numbers because of security reasons. Each function receives two tables as arguments. The first table contains the input data specified by clients. The second table is to contain the output data to send to clients.
In the scripting environment, the global variable "__kyototycoon__" is defined by default. It contains objects to access server resources. The most important one is "__kyototycoon__.db", which is the primary database managed by the server. "__kyototycoon__.dbs" is an array of databases managed by the server. The return value of each function must be "__kyototycoon__.RVSUCCESS" for success or a related code for error.
Because the server uses respective instances of the Lua processor for each native thread, you cannot use global variables to share data among sessions. Use a cache database managed by the server for that purpose.
The following is an example code.
kt = __kyototycoon__ db = kt.db -- log the start-up message if kt.thid == 0 then kt.log("system", "the Lua script has been loaded") end -- echo back the input data as the output data function echo(inmap, outmap) for key, value in pairs(inmap) do outmap[key] = value end return kt.RVSUCCESS end -- report the internal state of the server function report(inmap, outmap) outmap["__kyototycoon__.VERSION"] = kt.VERSION outmap["__kyototycoon__.thid"] = kt.thid outmap["__kyototycoon__.db"] = tostring(kt.db) for i = 1, #kt.dbs do local key = "__kyototycoon__.dbs[" .. i .. "]" outmap[key] = tostring(kt.dbs[i]) end local names = "" for name, value in pairs(kt.dbs) do if #names > 0 then names = names .. "," end names = names .. name end outmap["names"] = names return kt.RVSUCCESS end -- log a message function log(inmap, outmap) local kind = inmap.kind local message = inmap.message if not message then return kt.RVEINVALID end if not kind then kind = "info" end kt.log(kind, message) return kt.RVSUCCESS end -- store a record function set(inmap, outmap) local key = inmap.key local value = inmap.value if not key or not value then return kt.RVEINVALID end local xt = inmap.xt if not db:set(key, value, xt) then return kt.RVEINTERNAL end return kt.RVSUCCESS end -- remove a record function remove(inmap, outmap) local key = inmap.key if not key then return kt.RVEINVALID end if not db:remove(key) then local err = db:error() if err:code() == kt.Error.NOREC then return kt.RVELOGIC end return kt.RVEINTERNAL end return kt.RVSUCCESS end -- increment the numeric string value function increment(inmap, outmap) local key = inmap.key local num = inmap.num if not key or not num then return kt.RVEINVALID end local function visit(rkey, rvalue, rxt) rvalue = tonumber(rvalue) if not rvalue then rvalue = 0 end num = rvalue + num return num end if not db:accept(key, visit) then return kt.REINTERNAL end outmap.num = num return kt.RVSUCCESS end -- retrieve the value of a record function get(inmap, outmap) local key = inmap.key if not key then return kt.RVEINVALID end local value, xt = db:get(key) if value then outmap.value = value outmap.xt = xt else local err = db:error() if err:code() == kt.Error.NOREC then return kt.RVELOGIC end return kt.RVEINTERNAL end return kt.RVSUCCESS end -- store records at once function setbulk(inmap, outmap) local num = db:set_bulk(inmap) if num < 0 then return kt.RVEINTERNAL end outmap["num"] = num return kt.RVSUCCESS end -- remove records at once function removebulk(inmap, outmap) local keys = {} for key, value in pairs(inmap) do table.insert(keys, key) end local num = db:remove_bulk(keys) if num < 0 then return kt.RVEINTERNAL end outmap["num"] = num return kt.RVSUCCESS end -- retrieve records at once function getbulk(inmap, outmap) local keys = {} for key, value in pairs(inmap) do table.insert(keys, key) end local res = db:get_bulk(keys) if not res then return kt.RVEINTERNAL end for key, value in pairs(res) do outmap[key] = value end return kt.RVSUCCESS end -- move the value of a record to another function move(inmap, outmap) local srckey = inmap.src local destkey = inmap.dest if not srckey or not destkey then return kt.RVEINVALID end local keys = { srckey, destkey } local first = true local srcval = nil local srcxt = nil local function visit(key, value, xt) if first then srcval = value srcxt = xt first = false return kt.Visitor.REMOVE end if srcval then return srcval, srcxt end return kt.Visitor.NOP end if not db:accept_bulk(keys, visit) then return kt.REINTERNAL end if not srcval then return kt.RVELOGIC end return kt.RVSUCCESS end -- list all records function list(inmap, outmap) local cur = db:cursor() cur:jump() while true do local key, value, xt = cur:get(true) if not key then break end outmap[key] = value end return kt.RVSUCCESS end -- upcate all characters in the value of a record function upcase(inmap, outmap) local key = inmap.key if not key then return kt.RVEINVALID end local function visit(key, value, xt) if not value then return kt.Visitor.NOP end return string.upper(value) end if not db:accept(key, visit) then return kt.RVEINTERNAL end return kt.RVSUCCESS end -- prolong the expiration time of a record function survive(inmap, outmap) local key = inmap.key if not key then return kt.RVEINVALID end local function visit(key, value, xt) if not value then return kt.Visitor.NOP end outmap.old_xt = xt if xt > kt.time() + 3600 then return kt.Visitor.NOP end return value, 3600 end if not db:accept(key, visit) then return kt.RVEINTERNAL end return kt.RVSUCCESS end -- count words with the MapReduce framework function countwords(inmap, outmap) local function map(key, value, emit) local values = kt.split(value, " ") for i = 1, #values do local word = kt.regex(values[i], "[ .?!:;]", "") word = string.lower(word) if #word > 0 then if not emit(word, "") then return false end end end return true end local function reduce(key, iter) local count = 0 while true do local value = iter() if not value then break end count = count + 1 end outmap[key] = count return true end if not db:mapreduce(map, reduce, nil, kt.DB.XNOLOCK) then return kt.RVEINTERNAL end return kt.RVSUCCESS end
To call the above functions, execute the following commands.
$ ktremotemgr script -arg key1 value1 -arg key2 value2 echo $ ktremotemgr script report $ ktremotemgr script -arg kind info -arg message hello log $ ktremotemgr script -arg key one -arg value first set $ ktremotemgr script -arg key two -arg value second set $ ktremotemgr script -arg key three -arg value third set $ ktremotemgr script -arg key three remove $ ktremotemgr script -arg key two get $ ktremotemgr script list
Modules
kyototycoon | The scripting extension of Kyoto Tycoon. |