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



60
61
62
63
64
65
66
67
68
# File 'lib/tyrant_manager/tyrant_instance.rb', line 60

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



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

def home_dir
  @home_dir
end

#managerObject

the manager that is associated with this instance



55
56
57
# File 'lib/tyrant_manager/tyrant_instance.rb', line 55

def manager
  @manager
end

#nameObject (readonly)

the name of this instance



52
53
54
# File 'lib/tyrant_manager/tyrant_instance.rb', line 52

def name
  @name
end

Class Method Details

.loggerObject



13
14
15
# File 'lib/tyrant_manager/tyrant_instance.rb', line 13

def logger
  Logging::Logger[self]
end

.setup(dir) ⇒ Object

Create all the directories needed for a tyrant



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/tyrant_manager/tyrant_instance.rb', line 19

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



77
78
79
# File 'lib/tyrant_manager/tyrant_instance.rb', line 77

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

#configurationObject

load the configuration



84
85
86
87
88
89
90
# File 'lib/tyrant_manager/tyrant_instance.rb', line 84

def configuration
  unless defined? @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



355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/tyrant_manager/tyrant_instance.rb', line 355

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



373
374
375
# File 'lib/tyrant_manager/tyrant_instance.rb', line 373

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

#data_dirObject

The directory housing the database file



130
131
132
# File 'lib/tyrant_manager/tyrant_instance.rb', line 130

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.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/tyrant_manager/tyrant_instance.rb', line 137

def db_file( type = configuration.type )
  unless defined? @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



173
174
175
# File 'lib/tyrant_manager/tyrant_instance.rb', line 173

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)


403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/tyrant_manager/tyrant_instance.rb', line 403

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)


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

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

#log_fileObject

The log file



109
110
111
# File 'lib/tyrant_manager/tyrant_instance.rb', line 109

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

#loggerObject



70
71
72
# File 'lib/tyrant_manager/tyrant_instance.rb', line 70

def logger
  Logging::Logger[self]
end

#lua_extension_fileObject

The lua extension file to load on start



116
117
118
# File 'lib/tyrant_manager/tyrant_instance.rb', line 116

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



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

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



102
103
104
# File 'lib/tyrant_manager/tyrant_instance.rb', line 102

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

#pid_fileObject

The pid file



95
96
97
# File 'lib/tyrant_manager/tyrant_instance.rb', line 95

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

#replication_timestamp_fileObject

The lua extension file to load on start



123
124
125
# File 'lib/tyrant_manager/tyrant_instance.rb', line 123

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)


325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/tyrant_manager/tyrant_instance.rb', line 325

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



289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/tyrant_manager/tyrant_instance.rb', line 289

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.



185
186
187
188
189
190
191
192
193
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
# File 'lib/tyrant_manager/tyrant_instance.rb', line 185

def start_command
  unless defined? @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



348
349
350
# File 'lib/tyrant_manager/tyrant_instance.rb', line 348

def stat
  connection.stat
end

#stopObject

kill the proc



306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/tyrant_manager/tyrant_instance.rb', line 306

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



156
157
158
# File 'lib/tyrant_manager/tyrant_instance.rb', line 156

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



163
164
165
# File 'lib/tyrant_manager/tyrant_instance.rb', line 163

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