| 4167 | |
| 4168 | |
| 4169 | Future<Response> Master::Http::_operation( |
| 4170 | const SlaveID& slaveId, |
| 4171 | const Offer::Operation& operation) const |
| 4172 | { |
| 4173 | Try<Resources> required = protobuf::getConsumedResources(operation); |
| 4174 | |
| 4175 | if (required.isError()) { |
| 4176 | return BadRequest( |
| 4177 | "Invalid " + stringify(operation.type()) + " operation: " + |
| 4178 | required.error()); |
| 4179 | } |
| 4180 | |
| 4181 | Slave* slave = master->slaves.registered.get(slaveId); |
| 4182 | if (slave == nullptr) { |
| 4183 | return BadRequest("No agent found with specified ID"); |
| 4184 | } |
| 4185 | |
| 4186 | // The resources recovered by rescinding outstanding offers. |
| 4187 | Resources totalRecovered; |
| 4188 | |
| 4189 | // We pessimistically assume that what seems like "available" |
| 4190 | // resources in the allocator will be gone. This can happen due to |
| 4191 | // the race between the allocator scheduling an 'allocate' call to |
| 4192 | // itself vs master's request to schedule 'updateAvailable'. |
| 4193 | // We greedily rescind one offer at time until we've rescinded |
| 4194 | // enough offers to cover 'operation'. |
| 4195 | foreach (Offer* offer, utils::copy(slave->offers)) { |
| 4196 | // If rescinding the offer would not contribute to satisfying |
| 4197 | // the required resources, skip it. |
| 4198 | Resources recovered = offer->resources(); |
| 4199 | recovered.unallocate(); |
| 4200 | |
| 4201 | if (required.get() == required.get() - recovered) { |
| 4202 | continue; |
| 4203 | } |
| 4204 | |
| 4205 | totalRecovered += recovered; |
| 4206 | required.get() -= recovered; |
| 4207 | |
| 4208 | // We explicitly pass 'Filters()' which has a default 'refuse_seconds' |
| 4209 | // of 5 seconds rather than 'None()' here, so that we can virtually |
| 4210 | // always win the race against 'allocate' if these resources are to |
| 4211 | // be offered back to these frameworks. |
| 4212 | // NOTE: However it's entirely possible that these resources are |
| 4213 | // offered to other frameworks in the next 'allocate' and the filter |
| 4214 | // cannot prevent it. |
| 4215 | master->rescindOffer(offer, Filters()); |
| 4216 | |
| 4217 | // If we've rescinded enough offers to cover 'operation', we're done. |
| 4218 | Try<Resources> updatedRecovered = totalRecovered.apply(operation); |
| 4219 | if (updatedRecovered.isSome()) { |
| 4220 | break; |
| 4221 | } |
| 4222 | } |
| 4223 | |
| 4224 | // Propagate the 'Future<Nothing>' as 'Future<Response>' where |
| 4225 | // 'Nothing' -> 'Accepted' and Failed -> 'Conflict'. |
| 4226 | return master->apply(slave, operation) |
nothing calls this directly
no test coverage detected