Class: TyrantManager::TyrantInstance

Inherits:
Object
  • Object
show all
Defined in:
lib/tyrant_manager/tyrant_instance.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir) ⇒ TyrantInstance

Create an instance connected to the tyrant located in the given directory



55
56
57
58
59
60
61
62
63
# File 'lib/tyrant_manager/tyrant_instance.rb', line 55

def initialize( dir )
  @home_dir = File.expand_path( dir )
  @name     = File.basename( @home_dir )
  if File.exist?( self.config_file ) then
    configuration # force a load
  else
    raise Error, "#{home_dir} is not a valid archive. #{self.config_file} does not exist"
  end
end

Instance Attribute Details

#home_dirObject (readonly)

the full path to the instance home directory



44
45
46
# File 'lib/tyrant_manager/tyrant_instance.rb', line 44

def home_dir
  @home_dir
end

#managerObject

the manager that is associated with this instance



50
51
52
# File 'lib/tyrant_manager/tyrant_instance.rb', line 50

def manager
  @manager
end

#nameObject (readonly)

the name of this instance



47
48
49
# File 'lib/tyrant_manager/tyrant_instance.rb', line 47

def name
  @name
end

Class Method Details

.loggerObject



8
9
10
# File 'lib/tyrant_manager/tyrant_instance.rb', line 8

def logger
  Logging::Logger[self]
end

.setup(dir) ⇒ Object

Create all the directories needed for a tyrant



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/tyrant_manager/tyrant_instance.rb', line 14

def setup( dir )
  unless File.directory?( dir )
    logger.info "Creating directory #{dir}"
    FileUtils.mkdir_p( dir )
  end

  cfg = File.join( dir, TyrantManager.config_file_basename )
  instance_name = File.basename( dir )

  unless File.exist?( cfg )
    template = TyrantManager::Paths.data_path( "default_instance_config.rb" )
    logger.info "Creating default config file #{cfg}"
    File.open( cfg, "w+" ) do |f|
      f.write ERB.new( IO.read( template ) ).result( binding )
    end
  end

  %w[ ulog data lua log ].each do |subdir|
    subdir = File.join( dir, subdir )
    unless File.directory?( subdir ) then
      logger.info "Creating directory #{subdir}"
      FileUtils.mkdir subdir 
    end
  end

  return TyrantInstance.new( dir )
end

Instance Method Details

#config_fileObject

The configuration file for the instance



72
73
74
# File 'lib/tyrant_manager/tyrant_instance.rb', line 72

def config_file
  @config_file ||= File.join( home_dir, TyrantManager.config_file_basename ) 
end

#configurationObject

load the configuration



79
80
81
82
83
84
85
# File 'lib/tyrant_manager/tyrant_instance.rb', line 79

def configuration
  unless @configuration then
    eval(  IO.read( self.config_file ) )
    @configuration = Loquacious::Configuration.for( name )
  end
  return @configuration
end

#connectionObject

return a network connection to this instance



364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/tyrant_manager/tyrant_instance.rb', line 364

def connection
  host = configuration.host

  # you cannot connect to 0.0.0.0
  if host == "0.0.0.0" then
    host = "localhost"
  end
  tclass = Rufus::Tokyo::Tyrant

  if configuration.type == "table" then
    tclass = Rufus::Tokyo::TyrantTable
  end
  tclass.new( configuration.host, configuration.port.to_i )
end

#connection_stringObject

Return the host:port connection string of the instance



382
383
384
# File 'lib/tyrant_manager/tyrant_instance.rb', line 382

def connection_string
  "#{configuration.host}:#{configuration.port}"
end

#data_dirObject

The directory housing the database file



125
126
127
# File 'lib/tyrant_manager/tyrant_instance.rb', line 125

def data_dir
  @data_dir ||= append_to_home_if_not_absolute( configuration.data_dir )
end

#db_file(type = configuration.type) ⇒ Object

The full path to the database file.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/tyrant_manager/tyrant_instance.rb', line 132

def db_file( type = configuration.type )
  unless @db_file then
    @db_file = case type
               when "memory-hash" then "*"
               when "memory-tree" then "+"
               when "hash"        then File.join( data_dir, "#{name}.tch" )
               when "tree"        then File.join( data_dir, "#{name}.tcb" )
               when "fixed"       then File.join( data_dir, "#{name}.tcf" )
               when "table"       then File.join( data_dir, "#{name}.tct" )
               else
                 raise Error, "Unknown configuration type [#{configuration.type}]"
               end
  end
  return @db_file
end

#index_typesObject

Valid index styles as defined by Tokyo Cabinet

See Tokyo Cabinet source code, tctdbstrtoindex() in tctdb.c



182
183
184
# File 'lib/tyrant_manager/tyrant_instance.rb', line 182

def index_types
  %w[ lex lexical str dec decimal num tok token qgr qgram fts void optimize ]
end

#is_master_master?Boolean

Is this instance in a master-master relationship with another server. This means that it is a slave, and its master server is also a slave of this server

Returns:

  • (Boolean)


412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/tyrant_manager/tyrant_instance.rb', line 412

def is_master_master?
  if mc = self.master_connection then
    mstat = mc.stat
    mslave_host = manager.ip_of(mstat['mhost'])
    mslave_port = mstat['mport']

    mslave_conn_string = "#{mslave_host}:#{mslave_port}"

    my_host = manager.ip_of( configuration.host )
    my_conn_string = "#{my_host}:#{configuration.port}"
    logger.debug "#is_master_master? -> my_conn_string = #{my_conn_string} mslave_conn_string = #{mslave_conn_string}"

    return (my_conn_string == mslave_conn_string)
  end
  return false
end

#is_slave?Boolean

Is this instance a slave of another server? This means it could be in a master-slave or master-master relationship. returns true or false explicitly

Returns:

  • (Boolean)


391
392
393
394
# File 'lib/tyrant_manager/tyrant_instance.rb', line 391

def is_slave?
  s = connection.stat
  return ((s['mhost'] and s['mport']) ? true : false )
end

#log_fileObject

The log file



104
105
106
# File 'lib/tyrant_manager/tyrant_instance.rb', line 104

def log_file
  @log_file ||= append_to_home_if_not_absolute( configuration.log_file )
end

#loggerObject



65
66
67
# File 'lib/tyrant_manager/tyrant_instance.rb', line 65

def logger
  Logging::Logger[self]
end

#lua_extension_fileObject

The lua extension file



111
112
113
# File 'lib/tyrant_manager/tyrant_instance.rb', line 111

def lua_extension_file
  @lua_extension_file ||= append_to_home_if_not_absolute( configuration.lua_extension_file )
end

#master_connectionObject

return a network connection to the master server of this instance



399
400
401
402
403
404
405
# File 'lib/tyrant_manager/tyrant_instance.rb', line 399

def master_connection
  if is_slave? then
    s = self.stat
    return Rufus::Tokyo::Tyrant.new( s['mhost'], s['mport'].to_i )
  end
  return nil
end

#pidObject

The pid of the service



97
98
99
# File 'lib/tyrant_manager/tyrant_instance.rb', line 97

def pid
  Float( IO.read( pid_file ).strip ).to_i
end

#pid_fileObject

The pid file



90
91
92
# File 'lib/tyrant_manager/tyrant_instance.rb', line 90

def pid_file
  @pid_file ||= append_to_home_if_not_absolute( configuration.pid_file )
end

#replication_timestamp_fileObject

The replication timestamp file



118
119
120
# File 'lib/tyrant_manager/tyrant_instance.rb', line 118

def replication_timestamp_file
  @replication_timestamp_file ||= append_to_home_if_not_absolute( configuration.replication_timestamp_file )
end

#running?Boolean

check if process is alive

Returns:

  • (Boolean)


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/tyrant_manager/tyrant_instance.rb', line 334

def running?
  begin
    if File.exist?( self.pid_file ) then
      _pid = self.pid
      Process.kill( 0, _pid )
      return true
    else
      return false
    end
  rescue Errno::EPERM
    logger.info "Process #{_pid} is beyond my control"
  rescue Errno::ESRCH
    logger.info "Process #{_pid} is dead"
    cleanup_pid_file
    return false
  rescue => e
    logger.error "Problem sending kill(0, #{_pid}) : #{e}"
  end
end

#startObject

Start the tyrant



298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/tyrant_manager/tyrant_instance.rb', line 298

def start
  o = %x[ #{start_command} ]
  o.strip!
  logger.info o if o.length > 0 
  3.times do |x|
    if self.running? then
      logger.info "#{self.name} is running as pid #{self.pid}"
      break
    else
      sleep 0.25
    end
  end
end

#start_commandObject

Start command.

This is a bit convoluted to bring together all the options and put them into one big commandline item.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/tyrant_manager/tyrant_instance.rb', line 194

def start_command
  unless @start_command then
    ##-- ttserver executable
    parts = [ manager.configuration.ttserver ]

    ##-- host and port
    parts << "-host #{configuration.host}" if configuration.host
    parts << "-port #{configuration.port}" if configuration.port

    ##-- thread options
    if thnum = cascading_config( 'thread_count' ) then
      parts << "-thnum #{thnum}"
    end
    if tout = cascading_config( 'session_timeout' ) then
      parts << "-tout #{tout}"
    end

    ##-- daemoization and pid
    parts << "-dmn" if cascading_config( 'daemonize' )
    parts << "-pid #{pid_file}"


    ##-- logging
    parts << "-log #{log_file}"
    if log_level = cascading_config( 'log_level' ) then
      if log_level == "error" then
        parts << "-le"
      elsif log_level == "debug" then
        parts << "-ld" 
      elsif log_level == "info" then
        # leave it at info
      else
        raise Error, "Invalid log level setting [#{log_level}]"
      end
    end

    ##-- update logs
    parts << "-ulog #{ulog_dir}"
    if ulim = cascading_config( 'update_log_size' )then
      parts << "-ulim #{ulim}"
    end
    parts << "-uas" if cascading_config( 'update_log_async' )

    ##-- replication items, server id, master, replication timestamp file
    parts << "-sid #{configuration.server_id}"       if configuration.server_id
    parts << "-mhost #{configuration.master_server}" if configuration.master_server
    parts << "-mport #{configuration.master_port}"   if configuration.master_port
    parts << "-rts #{replication_timestamp_file}" if configuration.replication_timestamp_file
    parts << "-rcc" if configuration.replication_consistency_check

    ##-- lua extension
    if configuration.lua_extension_file then
      if File.exist?( lua_extension_file ) then
        parts << "-ext #{lua_extension_file}" 
        if pc = configuration.periodic_command then
          if pc.name and pc.period then
            parts << "-extpc #{pc.name} #{pc.period}"
          end
        end
      else
        logger.warn "lua extension file #{lua_extension_file} does not exist"
      end
    end

    ##-- command permissiosn
    if deny = cascading_config( "deny_commands" ) then
      parts << "-mask #{deny.join(",")}"
    end

    if allow = cascading_config( "allow_commands" ) then
      parts << "-unmask #{allow.join(",")}"
    end

    ##-- now for the filename.  The format is
    #  filename.ext#opts=ld#mode=wc#tuning_param=value#tuning_param=value#idx=field:dec...
    #
    file_pairs = []
    file_pairs << "opts=#{configuration.opts}"
    file_pairs << "mode=#{configuration.mode}"
    Loquacious::Configuration::Iterator.new( configuration.tuning_params ).each do |node|
      file_pairs << "#{node.name}=#{node.obj}" if node.obj
    end

    if configuration.type == "table" and configuration.indexes then
      Loquacious::Configuration::Iterator.new( configuration.indexes ).each do |index_node|
        if index_node.obj and index_types.include?( index_node.obj.downcase ) then
          file_pairs << "idx=#{index_node.name}:#{index_node.obj}"
        end
      end
    end

    file_name_and_params = "#{db_file}##{file_pairs.join("#")}"

    parts << file_name_and_params

    @start_command = parts.join( " " )
  end

  return @start_command
end

#statObject

return the stats for this instance



357
358
359
# File 'lib/tyrant_manager/tyrant_instance.rb', line 357

def stat
  connection.stat
end

#stopObject

kill the proc



315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/tyrant_manager/tyrant_instance.rb', line 315

def stop
  begin
    _pid = self.pid
    Process.kill( "TERM" , _pid )
    logger.info "Sent signal TERM to #{_pid}"
  rescue Errno::EPERM
    logger.info "Process #{_pid} is beyond my control"
  rescue Errno::ESRCH
    logger.info "Process #{_pid} is dead"
    cleanup_pid_file
 rescue => e
    logger.error "Problem sending kill(TERM, #{_pid}) : #{e}"
  end
end

#ulog_dirObject

The directory housing the database file



151
152
153
# File 'lib/tyrant_manager/tyrant_instance.rb', line 151

def ulog_dir
  @ulog_dir ||= append_to_home_if_not_absolute( configuration.ulog_dir )
end

#ulog_filesObject

The list of .ulog files that are in the ulog/ directory



158
159
160
# File 'lib/tyrant_manager/tyrant_instance.rb', line 158

def ulog_files
  Dir.glob( "#{ulog_dir}/*.ulog" ).sort
end