-----------------------------------------------------------
This is an automatically generated e-mail. To reply, visit:
https://reviews.apache.org/r/29736/
-----------------------------------------------------------
(Updated Feb. 5, 2015, 1:40 a.m.)
Review request for mesos, Adam B, Benjamin Hindman, Ben Mahler, Jie Yu, and
Vinod Kone.
Repository: mesos
Description (updated)
-------
1. A `Resources::Filter` constructs off of a predicate and the operator () has
a single implementation:
```cpp
Resources result;
foreach (const Resource& resource, resources) {
if (pred(resource)) {
result += resource;
}
}
return result;
```
Advantages of this approach:
a. No new class definition is necessary to create a new filter.
- We can use static functions to create a new filter inline. e.g.
Filter(persistentVolume) or Filter(empty)
- We can use lambdas (later on) to create a new filter inline.
Filter([](const Resource& resource) { ... })
- We don't need to write the same for loop with different predicates for
every new filter.
b. The resulting set of resources are guaranteed to consist only of resources
that existed in the original resources.
- The only guarantee we enforce with a derived filter class that inherits
from a base filter class is
that we take Resources and return Resources. Nothing enforces that the
resulting resources must only be
consisted of existing resources.
2. We can naturally compose filters with logical operators.
Suppose we want a filter that returns resources that are reserved for a
particular `ReservationType`, and
a filter that returns persistent volumes. It may look something like this in
the old approach.
```cpp
// Returns resources that are reserved for a particular ReservationType.
struct ReservedFilter : Filter
{
explicit ReservedFilter(const Resource::ReservationType& _reservationType)
: reservationType(_reservationType) {}
virtual Resources apply(const Resources& resources) const {
Resources result;
foreach (const Resource& resource, resources) {
if (isReserved(resource, reservationType)) {
result += resource;
}
}
return result;
}
Resource::ReservationType reservationType;
}
// Returns persistent volumes.
struct PersistentVolumeFilter : Filter
{
virtual Resources apply(const Resources& resources) const {
Resources result;
foreach (const Resource& resource, resources) {
if (isPersistentVolume(resource)) {
result += resource;
}
}
return result;
}
}
```
and we can use them like so:
```cpp
ReservedFilter(Resource::DYNAMIC).apply(resources);
PersistentVolumeFilter().apply(resources);
```
But how about when we want to now create a filter that returns resources that
are persistent volumes **and**
reserved for a particular `ReservationType`? Ideally we would compose these 2
existing filters somehow,
but we end up having to duplicate most of the code from each filter and write a
new filter definition
(or perform both filters and intersect the results, yikes.):
```cpp
// Returns persistent volumes that are reserved for a particular
ReservationType.
struct ReservedPersistentVolumeFilter : Filter
{
explicit ReservedFilter(const Resource::ReservationType& _reservationType)
: reservationType(_reservationType) {}
virtual Resources apply(const Resources& resources) const {
Resources result;
foreach (const Resource& resource, resources) {
if (reserved(resource, reservationType) && persistentVolume(resource)) {
result += resource;
}
}
return result;
}
Resource::ReservationType reservationType;
}
```
and it would be used like so:
```cpp
ReservedPersistentVolumeFilter(Resource::DYNAMIC).apply(resources);
```
Here's what the above looks like in the new design.
```cpp
class Filter
{
public:
// Useful predefined filters.
static Filter reserved(const Resource::ReservationType& reservationType)
{
return Filter(lambda::bind(Resources::reserved, lambda::_1,
reservationType));
}
static Filter persistentVolume()
{
return Filter(Resources::persistentVolume);
}
/* Generic Filter definition */
};
```
These can be used like so:
```cpp
Filter::reserved(Resource::DYNAMIC)(resources);
Filter::persistentVolume()(resources);
```
With logical operators, we can compose them inline like so:
```cpp
Filter filter =
Filter::reserved(Resource::DYNAMIC) && Filter::persistentVolume();
Resources filtered = filter(resources);
```
If this composition is common, then we can always add another static function
under `Filter`:
```cpp
class Filter
{
static Filter reserved(const Resource::ReservationType& reservationType);
static Filter persistentVolume();
static Filter reservedPersistentVolume(const Resource::ReservationType&
reservationType)
{
return Filter::reserved(Resource::DYNAMIC) && Filter::persistentVolume();
}
/* ... */
};
```
At which point we can use it like so:
```cpp
Filter::reservedPersistentVolume(Resource::DYNAMIC)(resources);
```
Diffs
-----
include/mesos/resources.hpp 3b57568c10233a0c692787de6464f21af5eaadf4
src/common/resources.cpp 308b8860fec3db5bfab3925423afda037bfbc4e7
src/master/hierarchical_allocator_process.hpp
6b4489276d9f72f3bd99066bf7e48dba5ebe537e
src/tests/hierarchical_allocator_tests.cpp
f44d9e98d6d9db9621f5361cddb6134f90277180
src/tests/resources_tests.cpp 4744e872b082553046ecc0e344403754ee685842
Diff: https://reviews.apache.org/r/29736/diff/
Testing
-------
make check
Thanks,
Michael Park