|
1 | 1 | require 'cloud_controller/deployment_updater/actions/scale_down_canceled_processes'
|
2 |
| -require 'cloud_controller/deployment_updater/actions/scale_down_old_process' |
3 | 2 | require 'cloud_controller/deployment_updater/actions/finalize'
|
| 3 | +require 'cloud_controller/deployment_updater/actions/down_scaler' |
| 4 | +require 'cloud_controller/deployment_updater/actions/up_scaler' |
| 5 | +require 'cloud_controller/diego/constants' |
4 | 6 |
|
5 | 7 | module VCAP::CloudController
|
6 | 8 | module DeploymentUpdater
|
7 | 9 | module Actions
|
8 | 10 | class Scale
|
9 |
| - HEALTHY_STATES = [VCAP::CloudController::Diego::LRP_RUNNING, VCAP::CloudController::Diego::LRP_STARTING].freeze |
10 |
| - attr_reader :deployment, :logger, :app |
| 11 | + attr_reader :deployment, :logger, :app, :target_total_instance_count, :interim_desired_instance_count |
11 | 12 |
|
12 |
| - def initialize(deployment, logger) |
| 13 | + def initialize(deployment, logger, target_total_instance_count, interim_desired_instance_count=nil) |
13 | 14 | @deployment = deployment
|
14 | 15 | @logger = logger
|
15 | 16 | @app = deployment.app
|
| 17 | + @target_total_instance_count = target_total_instance_count |
| 18 | + @interim_desired_instance_count = interim_desired_instance_count || target_total_instance_count |
16 | 19 | end
|
17 | 20 |
|
18 | 21 | def call
|
| 22 | + down_scaler = DownScaler.new(deployment, logger, target_total_instance_count, instance_count_summary.routable_instances_count) |
| 23 | + up_scaler = UpScaler.new(deployment, logger, interim_desired_instance_count, instance_count_summary) |
| 24 | + |
19 | 25 | deployment.db.transaction do
|
20 | 26 | return unless deployment.lock!.state == DeploymentModel::DEPLOYING_STATE
|
21 |
| - |
22 |
| - return unless can_scale? || can_downscale? |
| 27 | + return unless up_scaler.can_scale? || down_scaler.can_downscale? |
23 | 28 |
|
24 | 29 | app.lock!
|
25 | 30 |
|
26 | 31 | oldest_web_process_with_instances.lock!
|
27 | 32 | deploying_web_process.lock!
|
28 | 33 |
|
29 | 34 | deployment.update(
|
30 |
| - state: DeploymentModel::DEPLOYING_STATE, |
31 | 35 | status_value: DeploymentModel::ACTIVE_STATUS_VALUE,
|
32 | 36 | status_reason: DeploymentModel::DEPLOYING_STATUS_REASON
|
33 | 37 | )
|
34 | 38 |
|
35 |
| - if deploying_web_process.instances >= deployment.original_web_process_instance_count |
36 |
| - Finalize.new(deployment).call |
37 |
| - return |
38 |
| - end |
39 |
| - |
40 | 39 | ScaleDownCanceledProcesses.new(deployment).call
|
41 | 40 |
|
42 |
| - scale_down_old_processes if can_downscale? |
43 |
| - |
44 |
| - if can_scale? |
45 |
| - deploying_web_process.update(instances: desired_new_instances) |
46 |
| - deployment.update(last_healthy_at: Time.now) |
47 |
| - end |
48 |
| - end |
49 |
| - end |
50 |
| - |
51 |
| - private |
| 41 | + down_scaler.scale_down if down_scaler.can_downscale? |
52 | 42 |
|
53 |
| - def scale_down_old_processes |
54 |
| - instances_to_reduce = non_deploying_web_processes.map(&:instances).sum - desired_non_deploying_instances |
| 43 | + return true if up_scaler.finished_scaling? |
55 | 44 |
|
56 |
| - return if instances_to_reduce <= 0 |
57 |
| - |
58 |
| - non_deploying_web_processes.each do |process| |
59 |
| - if instances_to_reduce < process.instances |
60 |
| - ScaleDownOldProcess.new(deployment, process, process.instances - instances_to_reduce).call |
61 |
| - break |
62 |
| - end |
63 |
| - |
64 |
| - instances_to_reduce -= process.instances |
65 |
| - ScaleDownOldProcess.new(deployment, process, 0).call |
| 45 | + up_scaler.scale_up if up_scaler.can_scale? |
66 | 46 | end
|
67 |
| - end |
68 |
| - |
69 |
| - def can_scale? |
70 |
| - starting_instances.count < deployment.max_in_flight && |
71 |
| - unhealthy_instances.count == 0 && |
72 |
| - routable_instances.count >= deploying_web_process.instances - deployment.max_in_flight |
73 |
| - rescue CloudController::Errors::ApiError # the instances_reporter re-raises InstancesUnavailable as ApiError |
74 |
| - logger.info("skipping-deployment-update-for-#{deployment.guid}") |
75 | 47 | false
|
76 |
| - end |
77 |
| - |
78 |
| - def can_downscale? |
79 |
| - non_deploying_web_processes.map(&:instances).sum > desired_non_deploying_instances |
80 | 48 | rescue CloudController::Errors::ApiError # the instances_reporter re-raises InstancesUnavailable as ApiError
|
81 | 49 | logger.info("skipping-deployment-update-for-#{deployment.guid}")
|
82 | 50 | false
|
83 | 51 | end
|
84 | 52 |
|
85 |
| - def desired_non_deploying_instances |
86 |
| - [deployment.original_web_process_instance_count - routable_instances.count, 0].max |
87 |
| - end |
88 |
| - |
89 |
| - def desired_new_instances |
90 |
| - [routable_instances.count + deployment.max_in_flight, deployment.original_web_process_instance_count].min |
91 |
| - end |
| 53 | + private |
92 | 54 |
|
93 | 55 | def oldest_web_process_with_instances
|
| 56 | + # TODO: lock all web processes? We might alter all of them, depending on max-in-flight size |
94 | 57 | @oldest_web_process_with_instances ||= app.web_processes.select { |process| process.instances > 0 }.min_by { |p| [p.created_at, p.id] }
|
95 | 58 | end
|
96 | 59 |
|
97 |
| - def non_deploying_web_processes |
98 |
| - app.web_processes.reject { |process| process.guid == deploying_web_process.guid }.sort_by { |p| [p.created_at, p.id] } |
| 60 | + def instance_count_summary |
| 61 | + @instance_count_summary ||= instance_reporters.instance_count_summary(deploying_web_process) |
99 | 62 | end
|
100 | 63 |
|
101 | 64 | def deploying_web_process
|
102 | 65 | @deploying_web_process ||= deployment.deploying_web_process
|
103 | 66 | end
|
104 | 67 |
|
105 |
| - def starting_instances |
106 |
| - healthy_instances.reject { |_, val| val[:state] == VCAP::CloudController::Diego::LRP_RUNNING && val[:routable] } |
107 |
| - end |
108 |
| - |
109 |
| - def routable_instances |
110 |
| - reported_instances.select { |_, val| val[:state] == VCAP::CloudController::Diego::LRP_RUNNING && val[:routable] } |
111 |
| - end |
112 |
| - |
113 |
| - def healthy_instances |
114 |
| - reported_instances.select { |_, val| HEALTHY_STATES.include?(val[:state]) } |
115 |
| - end |
116 |
| - |
117 |
| - def unhealthy_instances |
118 |
| - reported_instances.reject { |_, val| HEALTHY_STATES.include?(val[:state]) } |
119 |
| - end |
120 |
| - |
121 |
| - def reported_instances |
122 |
| - @reported_instances = instance_reporters.all_instances_for_app(deploying_web_process) |
123 |
| - end |
124 |
| - |
125 | 68 | def instance_reporters
|
126 | 69 | CloudController::DependencyLocator.instance.instances_reporters
|
127 | 70 | end
|
|
0 commit comments