~hegde-shashank/appscale/more-ft

« back to all changes in this revision

Viewing changes to AppController/djinn.rb

  • Committer: root
  • Date: 2012-03-07 08:17:57 UTC
  • Revision ID: root@appscale-image0-20120307081757-6mc9gu3cv4m26ezq
Adding fault tolerance for appengine and using only a single watcher instead of a watcher for each role.
If a separate watcher is used for each role then if a client that created an ephemeral node being watched goes down then the connection to the zookeeper cluster is lost. (Is this a zk bug?)

Show diffs side-by-side

added added

removed removed

Lines of Context:
401
401
      change_job(secret)
402
402
    end
403
403
 
 
404
    zk_create_node_id
404
405
    register_roles
405
406
    setup_watchers
406
407
 
430
431
 
431
432
      if my_node.is_status_monitor?
432
433
        @nodes.each { |node|
433
 
          get_status(node)
 
434
          begin
 
435
            get_status(node)
 
436
          rescue Exception
 
437
            Djinn.log(Djinn::LOG_ERROR,"job_start - getting status", "Error")
 
438
          end
434
439
 
435
440
          if node.should_destroy?
436
441
            Djinn.log_debug("heartbeat - destroying node [#{node}]")
670
675
    @lock = Monitor.new
671
676
    @api_status = {}
672
677
 
673
 
    @watcher_callbacks = {}
 
678
    @watcher_callback
674
679
  end
675
680
 
676
681
  def get_login
1231
1236
 
1232
1237
    ZKInterface.init(my_node, @nodes)
1233
1238
 
 
1239
    if my_node.is_status_monitor?
 
1240
      zk_save_cluster_config(@creds['keyname'])
 
1241
    end
 
1242
 
1234
1243
    commands = {
1235
1244
      "load_balancer" => "start_load_balancer",
1236
1245
      "memcache" => "start_memcached",
1917
1926
 
1918
1927
    uac = UserAppClient.new(@userappserver_private_ip, @@secret)
1919
1928
 
 
1929
    if !(my_node.is_app_manager? or my_node.is_appengine?)
 
1930
      Djinn.log(Djinn::LOG_INFO,"start_appengine", "I am neither the appengine nor app manager.")
 
1931
      return
 
1932
    end
 
1933
 
1920
1934
    if @restored == false #and restore_from_db?
1921
1935
      Djinn.log_debug("need to restore")
1922
1936
      all_apps = uac.get_all_apps()
1938
1952
      Djinn.log_debug("don't need to restore")
1939
1953
    end
1940
1954
 
 
1955
    if !@app_names.nil?
 
1956
      Djinn.log(Djinn::LOG_TRACE,"start_appengine: app_names",@app_names.join(','))
 
1957
    else
 
1958
      Djinn.log(Djinn::LOG_TRACE,"start_appengine","@app_names is nil")
 
1959
    end
 
1960
 
 
1961
    if !@apps_loaded.nil?
 
1962
      Djinn.log(Djinn::LOG_TRACE,"start_appengine: apps_loaded",@apps_loaded.join(','))
 
1963
    else
 
1964
      Djinn.log(Djinn::LOG_TRACE,"start_appengine","@app_loaded is nil")
 
1965
    end
 
1966
 
1941
1967
    apps_to_load = @app_names - @apps_loaded - ["none"]
 
1968
    Djinn.log(Djinn::LOG_TRACE,"start_appengine: apps_to_load", apps_to_load.join(","))
1942
1969
    apps_to_load.each { |app|
1943
1970
      app_data = uac.get_app_data(app)
1944
1971
      Djinn.log_debug("Get app data for #{app} said [#{app_data}]")
2066
2093
      end
2067
2094
      
2068
2095
      Djinn.log_debug("Loading #{app}")
2069
 
      @apps_loaded << app
 
2096
      if my_node.is_appengine? or my_node.is_app_manager?
 
2097
        Djinn.log(Djinn::LOG_DEBUG,"start_appengine","Marking #{app} as loaded!")
 
2098
        @apps_loaded << app
 
2099
      end
2070
2100
    }
2071
2101
 
2072
2102
    Djinn.log_debug("#{apps_to_load.size} apps loaded")  
2232
2262
 
2233
2263
  def watch_role(role)
2234
2264
    Djinn.log(Djinn::LOG_TRACE, self.class, "Watching role #{role}")
2235
 
    if @watcher_callbacks[role].nil?
2236
 
      Djinn.log(Djinn::LOG_TRACE, "watch_role", "Creating a callback for #{role}")
2237
 
      @watcher_callbacks[role] = Zookeeper::WatcherCallback.new {
2238
 
        role_change_notification(@watcher_callbacks[role].context)
 
2265
    if @watcher_callback.nil?
 
2266
      Djinn.log(Djinn::LOG_TRACE, "watch_role", "Creating a callback")
 
2267
      @watcher_callback = Zookeeper::WatcherCallback.new {
 
2268
        role_change_notification(@watcher_callback.context)
2239
2269
      }
2240
2270
    end
2241
 
    zk_get_actors(role,@watcher_callbacks[role])
 
2271
    zk_get_actors(role,@watcher_callback)
2242
2272
  end
2243
2273
 
2244
2274
public
 
2275
  #TODO: Need to ignore notifications during the startup phase
2245
2276
  def role_change_notification(context)
2246
2277
    path = context[:path]
 
2278
    Djinn.log(Djinn::LOG_DEBUG,"role_change_notification","path: #{path}")
2247
2279
    # path returned would be of the form /roles/<role-name>
2248
2280
    ar = path.split('/')
 
2281
    #Djinn.log(Djinn::LOG_DEBUG,"role_change_notification",ar)
2249
2282
    role = ar[-1]
2250
2283
    Djinn.log(Djinn::LOG_TRACE, self.class, "Received a role change notification for #{role}")
2251
2284
    Djinn.log(Djinn::LOG_INFO, "profile", "Detected changes to #{role}")
2252
2285
 
2253
 
    new_actors = zk_get_actors(role, @watcher_callbacks[role])
 
2286
    new_actors = zk_get_actors(role, @watcher_callback)
2254
2287
    Djinn.log(Djinn::LOG_DEBUG,"role_change_notification","actors of #{role}: #{new_actors}")
2255
2288
 
2256
2289
    if (role == DjinnJobData::ROLE_STATUS_MTR) or (role == DjinnJobData::ROLE_APP_MGR) or (role == DjinnJobData::ROLE_NEPTUNE_MGR)
2261
2294
    end
2262
2295
 
2263
2296
    if (my_node.is_status_monitor? and role == DjinnJobData::ROLE_APP_ENGINE)
2264
 
      start_new_app_engine
 
2297
      start_new_app_engine(new_actors)
2265
2298
    end
2266
2299
 
2267
2300
    # Nodes might have changed after leader election. So get the latest nodes' state from zk
2338
2371
    return neptune_nodes
2339
2372
  end
2340
2373
 
2341
 
  def start_new_app_engine
 
2374
  def start_new_app_engine(appengines)
2342
2375
    Djinn.log(Djinn::LOG_TRACE,self.class,"Starting a new app engine")
2343
2376
 
 
2377
    required_appengines = ZKInterface.get(ROLE_PATH+"/#{DjinnJobData::ROLE_APP_ENGINE}")
 
2378
    required_appengines = required_appengines.to_i
 
2379
    Djinn.log(Djinn::LOG_INFO,"start_new_app_engine","Need #{required_appengines} app engines. I have #{appengines.size} app engines.")
 
2380
    if appengines.size >= required_appengines
 
2381
      return
 
2382
    end
 
2383
 
 
2384
    # Make sure that the nodes designated as appengines are indeed down
 
2385
    # If the ephemeral node does not exist then the node is really down
 
2386
    appengine_count = 0
 
2387
    keyname = @creds["keyname"]
 
2388
    nodes = zk_get_nodes(keyname)
 
2389
    nodes.each { |node|
 
2390
      if node.is_appengine?
 
2391
        path = "/#{keyname}/nodes/#{node.private_ip}/id"
 
2392
        ret = ZKInterface.get(path)
 
2393
        appengine_count += 1 if ret != ZKInterface::FAILURE
 
2394
      end
 
2395
    }
 
2396
    if appengine_count >= required_appengines
 
2397
      Djinn.log(Djinn::LOG_INFO,"start_new_app_engine","There are enough nodes designated as app engines. The dev app server may not be up yet.")
 
2398
      return
 
2399
      Djinn.log(Djinn::LOG_DEBUG,"start_new_app_engine","Not returning!")
 
2400
    end
 
2401
 
2344
2402
    # Find open nodes and start app engine
2345
 
    keyname = @creds["keyname"]
2346
 
    nodes = zk_get_nodes(keyname)
2347
2403
    new_app_engine_node = nil
2348
2404
    node_index = 0
2349
2405
    nodes.each { |node|
2355
2411
      node_index += 1
2356
2412
    }
2357
2413
 
2358
 
    return if new_app_engine_node.nil?
 
2414
    if new_app_engine_node.nil?
 
2415
      Djinn.log(Djinn::LOG_TRACE,"start_new_app_engine","No open nodes found")
 
2416
      return
 
2417
    end
2359
2418
 
2360
2419
    new_app_engine_node.add_roles(DjinnJobData::ROLE_APP_ENGINE)
2361
2420
    @nodes[node_index] = new_app_engine_node
2453
2512
    return actors
2454
2513
  end
2455
2514
 
 
2515
  # This method creates an ephemeral node to indicate that the node is up
 
2516
  def zk_create_node_id
 
2517
    Djinn.log(Djinn::LOG_TRACE,self.class,"Creating the node id")
 
2518
    keyname = @creds['keyname']
 
2519
    path = "/#{keyname}/nodes"
 
2520
    ZKInterface.ensure_path(path)
 
2521
    path = "/#{keyname}/nodes/#{my_node.private_ip}"
 
2522
    ZKInterface.ensure_path(path)
 
2523
    path = "/#{keyname}/nodes/#{my_node.private_ip}/id"
 
2524
    ZKInterface.set(path,ZKInterface::DEFAULT_DATA,ZKInterface::EPHEMERAL)
 
2525
  end
 
2526
 
2456
2527
  # Save the node into zookeeper
2457
2528
  def zk_save_node(keyname, node)
2458
2529
 
2556
2627
      nodes << node
2557
2628
    }
2558
2629
    @my_index = nil
 
2630
    return @nodes if nodes.size == 0
2559
2631
    return nodes
2560
2632
  end
2561
2633
 
2566
2638
    ZKInterface.delete(path)
2567
2639
  end
2568
2640
 
 
2641
  def zk_save_cluster_config(keyname)
 
2642
    Djinn.log(Djinn::LOG_TRACE,self.class,"Saving cluster config")
 
2643
 
 
2644
    roles = {}
 
2645
    @nodes.each { |node|
 
2646
      jobs = node.jobs
 
2647
      jobs.each { |job|
 
2648
        if roles[job].nil?
 
2649
          roles[job] = 1
 
2650
        else
 
2651
          roles[job] += 1
 
2652
        end
 
2653
      }
 
2654
    }
 
2655
 
 
2656
    role_names = roles.keys
 
2657
    role_names.each { |name|
 
2658
      puts "#{name} : #{roles[name]}"
 
2659
      ZKInterface.ensure_path(ROLE_PATH)
 
2660
      ZKInterface.set(ROLE_PATH+"/#{name}",roles[name].to_s,ZKInterface::NOT_EPHEMERAL)
 
2661
    }
 
2662
  end
 
2663
 
2569
2664
end