[openstack-dev] [cross-project][quotas][delimiter]My thoughts on how Delimiter uses generation-id for sequencing

Amrith Kumar amrith at tesora.com
Sun May 15 17:55:13 UTC 2016


Qijing,

As a simple example, let's assume that I use this schema. I realize that
it does not provide the resource provider thing that Jay talked about in
a previous (couple of weeks ago) email, but I believe that it serves to
illustrate how the generations are used.

create table resources (
    resource_id varchar(36) primary key,
    resource    varchar(32),
    generation  integer
) engine=innodb;

create table allocations (
       consumer_id       varchar(36),
       resource_id       varchar(36),
       amount            integer,
       foreign key (resource_id)
               references resources(resource_id)
) engine=innodb;

I've also populated it with this sample data.

insert into resources values ('b587d300-1a94-11e6-8478-000c291e9f7b',
'memory', 3);
insert into resources values ('b587ddb1-1a94-11e6-8478-000c291e9f7b',
'cpu', 3);
insert into resources values ('b587de7d-1a94-11e6-8478-000c291e9f7b',
'disk', 3);

insert into allocations values
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587d300-1a94-11e6-8478-000c291e9f7b', 1024 ),
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587ddb1-1a94-11e6-8478-000c291e9f7b',    6 ),
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587de7d-1a94-11e6-8478-000c291e9f7b',10240 ),
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587d300-1a94-11e6-8478-000c291e9f7b', 2048 ),
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587ddb1-1a94-11e6-8478-000c291e9f7b',    2 ),
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587de7d-1a94-11e6-8478-000c291e9f7b',  512 ),
( 'be03c4f7-1a96-11e6-8478-000c291e9f7b',
'b587d300-1a94-11e6-8478-000c291e9f7b', 2048 ),
( 'be03c4f7-1a96-11e6-8478-000c291e9f7b',
'b587ddb1-1a94-11e6-8478-000c291e9f7b',    2 ),
( 'be03c4f7-1a96-11e6-8478-000c291e9f7b',
'b587de7d-1a94-11e6-8478-000c291e9f7b',  512 );


That gives me this as a starting point.

mysql> select distinct resource from resources;
+----------+
| resource |
+----------+
| memory   |
| cpu      |
| disk     |
+----------+
3 rows in set (0.00 sec)

mysql> select distinct consumer_id from allocations;
+--------------------------------------+
| consumer_id                          |
+--------------------------------------+
| 61412e76-1a95-11e6-8478-000c291e9f7b |
| be03c4f7-1a96-11e6-8478-000c291e9f7b |
+--------------------------------------+
2 rows in set (0.00 sec)


-----

Assume that the consumer (61412e76-1a95-11e6-8478-000c291e9f7b) has a
CPU quota of 12, we can see that the user has not yet hit his quota.

mysql> select sum(amount) from resources, allocations where
resources.resource_id = allocations.resource_id and resources.resource =
'cpu' and consumer_id = '61412e76-1a95-11e6-8478-000c291e9f7b';
+-------------+
| sum(amount) |
+-------------+
|           8 |
+-------------+
1 row in set (0.00 sec)


In this situation, assume that this consumer wishes to consume two
CPU's. Here's what quota library would do.

The caller of quota library would provide something like:

	consumer_id: 61412e76-1a95-11e6-8478-000c291e9f7b
	resource: cpu
	quota: 12
	request: 2

Here's what the quota library would do.

mysql> select resources.resource_id, generation, sum(amount) from
resources, allocations where resources.resource_id =
allocations.resource_id and resources.resource = 'cpu' and consumer_id =
'61412e76-1a95-11e6-8478-000c291e9f7b' group by resources.resource_id,
generation\g
+--------------------------------------+------------+-------------+
| resource_id                          | generation | sum(amount) |
+--------------------------------------+------------+-------------+
| b587ddb1-1a94-11e6-8478-000c291e9f7b |          3 |           8 |
+--------------------------------------+------------+-------------+
1 row in set (0.00 sec)

-- it can now determine that the quota of 12 won't be violated by
allocating two more. So it goes ahead and does this.

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into allocations values
( '61412e76-1a95-11e6-8478-000c291e9f7b',
'b587ddb1-1a94-11e6-8478-000c291e9f7b', 2);
Query OK, 1 row affected (0.00 sec)

And then does this:

mysql> update resources set generation = generation + 1
    -> where resource_id = 'b587ddb1-1a94-11e6-8478-000c291e9f7b'
    -> and generation = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

It observes that 1 row was matched, so the allocation succeeded and
therefore it does this.

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

-------

Assume now that consumer 'be03c4f7-1a96-11e6-8478-000c291e9f7b' with a
cpu quota of 50 comes along and wants 4 more. The library does this.


mysql> select resources.resource_id, generation, sum(amount) from
resources, allocations where resources.resource_id =
allocations.resource_id and resources.resource = 'cpu' and consumer_id =
'be03c4f7-1a96-11e6-8478-000c291e9f7b' group by resources.resource_id,
generation;
+--------------------------------------+------------+-------------+
| resource_id                          | generation | sum(amount) |
+--------------------------------------+------------+-------------+
| b587ddb1-1a94-11e6-8478-000c291e9f7b |          4 |           2 |
+--------------------------------------+------------+-------------+
1 row in set (0.00 sec)


Clearly the user has only two cores in use and 4 more will not violate
the quota.

Therefore the quota library does this.

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into allocations values (
    -> 'be03c4f7-1a96-11e6-8478-000c291e9f7b',
    -> 'b587ddb1-1a94-11e6-8478-000c291e9f7b',
    -> 4);
Query OK, 1 row affected (0.00 sec)

mysql> update resources set generation = generation + 1 where
resource_id = 'b587ddb1-1a94-11e6-8478-000c291e9f7b' and generation = 4;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

Since, in the period of time between the select of the generations and
the time when the quota library decided to finish this allocation, some
other requester had made an allocation of CPU, the query updated 0 rows.
This is an error and therefore the quota library will rollback and
retry.

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select resources.resource_id, generation, sum(amount) from
resources, allocations where resources.resource_id =
allocations.resource_id and resources.resource = 'cpu' and consumer_id =
'be03c4f7-1a96-11e6-8478-000c291e9f7b' group by resources.resource_id,
generation;
+--------------------------------------+------------+-------------+
| resource_id                          | generation | sum(amount) |
+--------------------------------------+------------+-------------+
| b587ddb1-1a94-11e6-8478-000c291e9f7b |          5 |           2 |
+--------------------------------------+------------+-------------+
1 row in set (0.00 sec)


-- it sees that the generation is now 5

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into allocations values
( 'be03c4f7-1a96-11e6-8478-000c291e9f7b',
'b587ddb1-1a94-11e6-8478-000c291e9f7b', 4);
Query OK, 1 row affected (0.00 sec)

mysql> update resources set generation = generation + 1 where
resource_id = 'b587ddb1-1a94-11e6-8478-000c291e9f7b' and generation = 5;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

This time it works, 1 row was updated, so we commit.

mysql> commit;
Query OK, 0 rows affected (0.01 sec)


Treat freeing of a resource identically. First delete rows from
allocations and in the same transaction attempt to update resources
again. If you get a row updated, commit and if no row is updated,
rollback and try again.

Hope this helps!

-amrith


On Sun, 2016-05-15 at 01:16 -0700, Qijing Li wrote:
> Hi Vilobh,  
> 
> Here is my thoughts on how Delimiter uses generation-id to guarantee
>  sequencing. Please correct me if I understand it wrong.
> 
> First, the Delimiter need to introduce another model ResourceProvider
> who has two attributes:
> 
>       * resource_id 
>       * generation_id
> 
> The followings are the steps of how to consume a quota:
> 
> Step 1. Check if there is enough available quota 
> 
>     If yes, then get the $generation_id  by querying the model
> ResourceProvider with the given resource_id which is the point in time
> view of resource usage.
> 
>     If no, terminate the process of consuming the quota and return the
> message of “No enough quotas available."
> 
> Step 2. Consume the quota.
> 
>    2.1 Begin transaction
> 
>    2.2 Update the QuotaUsage model: QuotaUsage.in_use =
> QuotaUsage.in_use + amount of quota requested.
> 
>    2.3 Get the $generation_id by querying the ResourceProvider by the
> given resource_id.
> 
>         If the $generation_id is larger than the $generation_id in
> Step 1, then roll back transaction and GOTO step 1.
> 
>            this case means there is someone else has changed the
> QuotaUsage during this process.
> 
>         If the $generation_id is the same as the $generation_id in
> Step 1, then increase the ResourceProvider.generation_id by one and
> 
>         Commit the transaction. Done!
> 
>         Note: no case the $generation_id is less than the
> $generation_id in Step 1 because the $generation_id is nondecreasing.
> 
> 
> — Qijing
> 
> 
> __________________________________________________________________________
> OpenStack Development Mailing List (not for usage questions)
> Unsubscribe: OpenStack-dev-request at lists.openstack.org?subject:unsubscribe
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 966 bytes
Desc: This is a digitally signed message part
URL: <http://lists.openstack.org/pipermail/openstack-dev/attachments/20160515/affa567c/attachment.pgp>


More information about the OpenStack-dev mailing list