Bug 1391923 - Concurrent attempts to generate an ueber cert for an owner results in bad data
Summary: Concurrent attempts to generate an ueber cert for an owner results in bad data
Keywords:
Status: CLOSED CURRENTRELEASE
Alias: None
Product: Candlepin
Classification: Community
Component: candlepin
Version: 2.0
Hardware: Unspecified
OS: Unspecified
high
medium
Target Milestone: ---
: 2.0
Assignee: Michael Stead
QA Contact: Katello QA List
URL:
Whiteboard:
Depends On: 1391922
Blocks:
TreeView+ depends on / blocked
 
Reported: 2016-11-04 11:55 UTC by Michael Stead
Modified: 2019-12-16 07:18 UTC (History)
7 users (show)

Fixed In Version: candlepin-2.0.20-1
Doc Type: If docs needed, set a value
Doc Text:
Clone Of: 1391922
Environment:
Last Closed: 2016-11-16 09:12:05 UTC


Attachments (Terms of Use)
A script to create a new owner and concurrently run ueber cert generation. (738 bytes, application/x-shellscript)
2016-11-04 11:55 UTC, Michael Stead
no flags Details


Links
System ID Private Priority Status Summary Last Updated
Github candlepin candlepin pull 1388 0 None None None 2016-11-14 15:56:44 UTC

Description Michael Stead 2016-11-04 11:55:15 UTC
Created attachment 1217382 [details]
A script to create a new owner and concurrently run ueber cert generation.

+++ This bug was initially created as a clone of Bug #1391922 +++

Description of problem:

When concurrent requests to generate a new ueber cert for the same owner occur, a request can fail resulting in bad data in the DB. Once this happens, it prevents clients from re-generating, and fetching the cert.

Candlepin requires better transaction management when generating the cert and should lock the Owner to ensure that only a single cert is created per owner.

How reproducible:
100%

Steps to Reproduce:
1. Start with a fresh candlepin database.
2. Run the attached script to create a new owner, and concurrently run the ueber cert generation API. It may take a few runs of the script to get the bug to occur. Specify a new owner each time.


Actual results:

After running the script above, exceptions are thrown and the ueber cert data in the candlepin database is incomplete, which prevents generating a new cert.


$ ./concurrent_ueber_cert.sh -o mtest
Creating owner mtest and generating certs...
{
  "parentOwner" : null,
  "id" : "402882e7582b929701582f06a6a32b18",
  "key" : "mtest",
  "displayName" : "mtest",
  "contentPrefix" : null,
  "defaultServiceLevel" : null,
  "upstreamConsumer" : null,
  "logLevel" : null,
  "autobindDisabled" : null,
  "href" : "/owners/mtest",
  "created" : "2016-11-04T11:08:34.851+0000",
  "updated" : "2016-11-04T11:08:34.851+0000"
}[mstead@bluestar temp]$ {
{
  "displayMessage" : "Problem generating ueber cert for owner org.hibernate.NonUniqueResultException: query did not return a unique result: 2",
  "displayMessage" : "Problem generating ueber cert for owner org.hibernate.NonUniqueResultException: query did not return a unique result: 2",
  "requestUuid" : "0ad932f6-08e8-4e9e-9d35-58890040ac64"
  "requestUuid" : "60a07366-ea56-40d0-8742-a28c48542dc9"
}}

As shown in the results, the two requests collide causing hibernate exceptions. The resulting data (below) shows partial commits to the DB. The query should show a single set of ueber cert data per Org.


candlepin=# SELECT (SELECT Count(*)
        FROM   cp_subscription s
               INNER JOIN cp_product p
                       ON s.product_id = p.id
               LEFT OUTER JOIN cp_pool pool
                            ON pool.productid = p.id
        WHERE  p.NAME LIKE '%_ueber_product')    AS UEBER_SUBS,
       (SELECT Count(*)
        FROM   cp_ent_certificate ec
               INNER JOIN cp_entitlement e
                       ON e.id = ec.entitlement_id
               INNER JOIN cp_pool p
                       ON e.pool_id = p.id
               INNER JOIN cp_product prod
                       ON prod.id = p.productid
        WHERE  prod.NAME LIKE '%_ueber_product') AS UEBER_ENT_CERTS,
       (SELECT Count(*)
        FROM   cp_entitlement e
               INNER JOIN cp_pool p
                       ON e.pool_id = p.id
               INNER JOIN cp_product prod
                       ON prod.id = p.productid
        WHERE  prod.NAME LIKE '%_ueber_product') AS UEBER_ENTS,
        WHERE  NAME = 'ueber_cert_consumer')     AS UEBER_CONSUMER;  
 ueber_subs | ueber_ent_certs | ueber_ents | ueber_pools | ueber_content | ueber_products 
| ueber_consumer 
------------+-----------------+------------+-------------+---------------+----------------
+----------------
          2 |               0 |          0 |           2 |             2 |              2 
|              2
(1 row)

NOTE: The query result should have been 1 for each column.


Expected results:

After the above errors, we are unable to get the ueber cert for the target owner.

$ curl -k -u admin:admin https://localhost:8443/candlepin/owners/mtest/uebercert
{
  "displayMessage" : "Runtime Error query did not return a unique result: 2 at org.hibernate.internal.AbstractQueryImpl.uniqueElement:914",
  "requestUuid" : "dc9fdfbd-8707-4b53-ae51-d25952352d78"
}


We are also unable to generate a new one:

$ curl -k -u admin:admin -X POST https://localhost:8443/candlepin/owners/mtest/uebercert
{
  "displayMessage" : "Problem generating ueber cert for owner org.hibernate.NonUniqueResultException: query did not return a unique result: 2",
  "requestUuid" : "bac32cb7-9497-4eb0-832d-775fd1b9efa9"
}

Expected results:

Concurrent requests should succeed, and result in a single ueber certificate per Owner. The DB query above should show an equal count X per column, where X is the number of Owners that have an ueber cert generated.

Clients should be able to both fetch an existing cert and generate a new cert. 


Additional info:

NOTE: It may take a few runs of the script to get the bug to occur. Specify a new owner each time with -o NEW_OWNER_KEY

Comment 1 Michael Stead 2016-11-04 11:57:36 UTC
+++ NOTE +++

The validation query will be different for 2.0 due to the data model / DB Schema changes in 2.0.

I will update this BZ with the new query once the bug is fixed in 2.0

Comment 2 Michael Stead 2016-11-10 19:22:13 UTC
+++ Updated Validation Query +++

SELECT 
       -- NO SUBS FOR 2.0
       
       -- Certificates
       (SELECT Count(*)
        FROM   cp_ent_certificate ec
               INNER JOIN cp_entitlement e
                       ON e.id = ec.entitlement_id
               INNER JOIN cp_pool p
                       ON e.pool_id = p.id
               INNER JOIN cp2_products prod
                       ON prod.uuid = p.product_uuid
        WHERE  prod.NAME LIKE '%_ueber_product') AS UEBER_ENT_CERTS,
        
        -- Entitlements
       (SELECT Count(*)
        FROM   cp_entitlement e
               INNER JOIN cp_pool p
                       ON e.pool_id = p.id
               INNER JOIN cp2_products prod
                       ON prod.uuid = p.product_uuid
        WHERE  prod.NAME LIKE '%_ueber_product') AS UEBER_ENTS,
        
        -- Pools
       (SELECT Count(*)
        FROM   cp_pool p
               INNER JOIN cp2_products prod
                       ON prod.uuid = p.product_uuid
        WHERE  prod.NAME LIKE '%_ueber_product') AS UEBER_POOLS,
        
        -- Content
        (select count(*) from cp2_content c
               inner join cp2_product_content pc on pc.content_uuid=c.uuid
               inner join cp2_products p on p.uuid=pc.product_uuid
               where p.name LIKE '%ueber_product'
        ) AS CP2_UEBER_CONTENT,

        -- Owner Content
        (select count(*) from cp2_owner_content oc
               inner join cp2_content c on c.uuid=oc.content_uuid
               inner join cp2_product_content pc on pc.content_uuid=c.uuid
               inner join cp2_products p on p.uuid=pc.product_uuid
               where p.name LIKE '%ueber_product'
         ) as CP2_OWNER_CONTENT,

         -- Products
         (select count(*) from cp2_products p where p.name LIKE '%ueber_product') AS CP2_PRODUCTS,

         -- Owner Products
         (select count(*) from cp2_owner_products op
             INNER JOIN cp2_products prod on op.product_uuid = prod.uuid
             WHERE prod.name LIKE '%ueber_product'
         ) AS CP2_OWNER_PRODUCTS,

         -- Consumers
         (SELECT Count(*)
             FROM   cp_consumer
             WHERE  NAME = 'ueber_cert_consumer'
         ) AS UEBER_CONSUMER;


Note You need to log in before you can comment on or make changes to this bug.