From d5e7f6bea7eae0dc0f50a4fd3049799c163945c6 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Fri, 6 Nov 2015 16:10:31 -0500 Subject: [PATCH 1/4] resolve migration branches and run initdb --- ...27d36939e4_separate_v1_and_v2_checksums.py | 2 +- ...add_support_for_quay_s_security_indexer.py | 2 +- test/data/test.db | Bin 905216 -> 913408 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/migrations/versions/2827d36939e4_separate_v1_and_v2_checksums.py b/data/migrations/versions/2827d36939e4_separate_v1_and_v2_checksums.py index 4e161daed..f3ee69d0e 100644 --- a/data/migrations/versions/2827d36939e4_separate_v1_and_v2_checksums.py +++ b/data/migrations/versions/2827d36939e4_separate_v1_and_v2_checksums.py @@ -8,7 +8,7 @@ Create Date: 2015-11-04 16:29:48.905775 # revision identifiers, used by Alembic. revision = '2827d36939e4' -down_revision = '73669db7e12' +down_revision = '5cdc2d819c5' from alembic import op import sqlalchemy as sa diff --git a/data/migrations/versions/57dad559ff2d_add_support_for_quay_s_security_indexer.py b/data/migrations/versions/57dad559ff2d_add_support_for_quay_s_security_indexer.py index 7cd0f84c4..ed317f327 100644 --- a/data/migrations/versions/57dad559ff2d_add_support_for_quay_s_security_indexer.py +++ b/data/migrations/versions/57dad559ff2d_add_support_for_quay_s_security_indexer.py @@ -6,7 +6,7 @@ Create Date: 2015-07-13 16:51:41.669249 # revision identifiers, used by Alembic. revision = '57dad559ff2d' -down_revision = '35f538da62' +down_revision = '73669db7e12' from alembic import op import sqlalchemy as sa diff --git a/test/data/test.db b/test/data/test.db index ab3922d086edcb112797222c8805b7fabe1045e7..a301a7fa7b9c497a92d2d79db7742a6ae57927e0 100644 GIT binary patch delta 31822 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
L&JfgkT$W30>zfW2ye?zW!^dUbXwhj1 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 From 9036ca2f2fba0c64a6debb8c5140138cbdbc429f Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Thu, 5 Nov 2015 16:28:12 -0500 Subject: [PATCH 2/4] Backfill the v1 checksums from imagestorage --- ...722_backfill_parent_id_and_v1_checksums.py | 19 +++ util/migrate/__init__.py | 60 ++++++++++ util/migrate/backfill_content_checksums.py | 108 ++++++++++++++++++ util/migrate/backfill_v1_checksums.py | 70 ++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py create mode 100644 util/migrate/backfill_content_checksums.py create mode 100644 util/migrate/backfill_v1_checksums.py diff --git a/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py b/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py new file mode 100644 index 000000000..e6d732dcc --- /dev/null +++ b/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py @@ -0,0 +1,19 @@ +"""Backfill parent id and v1 checksums + +Revision ID: 22af01f81722 +Revises: 2827d36939e4 +Create Date: 2015-11-05 16:24:43.679323 + +""" + +# revision identifiers, used by Alembic. +revision = '22af01f81722' +down_revision = '2827d36939e4' + +from util.migrate.backfill_v1_checksums import backfill_checksums + +def upgrade(tables): + backfill_checksums() + +def downgrade(tables): + pass diff --git a/util/migrate/__init__.py b/util/migrate/__init__.py index 6b0a65feb..e1770742d 100644 --- a/util/migrate/__init__.py +++ b/util/migrate/__init__.py @@ -1,5 +1,12 @@ +import logging + from sqlalchemy.types import TypeDecorator, Text from sqlalchemy.dialects.mysql import TEXT as MySQLText, LONGTEXT +from random import shuffle + + +logger = logging.getLogger(__name__) + class UTF8LongText(TypeDecorator): """ Platform-independent UTF-8 LONGTEXT type. @@ -14,3 +21,56 @@ class UTF8LongText(TypeDecorator): return dialect.type_descriptor(LONGTEXT(charset='utf8mb4', collation='utf8mb4_unicode_ci')) else: return dialect.type_descriptor(Text()) + + +def _chance_duplication(pop_size, samples): + """ The chance of randomly selecting a duplicate when you choose the specified number of samples + from the specified population size. + """ + pairs = (samples * (samples - 1)) / 2.0 + unique = (pop_size - 1.0)/pop_size + all_unique = pow(unique, pairs) + return 1 - all_unique + + +def _num_checks(pop_size, desired): + """ Binary search for the proper number of entries to use to get the specified collision + probability. + """ + s_max = pop_size + s_min = 0 + last_test = -1 + s_test = s_max + + while s_max > s_min and last_test != s_test: + last_test = s_test + s_test = (s_max + s_min)/2 + chance = _chance_duplication(pop_size, s_test) + if chance > desired: + s_max = s_test - 1 + else: + s_min = s_test + + return s_test + + +def yield_random_entries(batch_query, batch_size, collision_chance): + """ This method will yield semi-random items from a query in a database friendly way until no + more items match the base query modifier. It will pull batches of batch_size from the query + and yield enough items from each batch so that concurrent workers have a reduced chance of + selecting the same items. For example, if your batches return 10,000 entries, and you desire + only a .03 collision_chance, we will only use 25 random entries before going back to the db + for a new batch. + """ + + # Seed with some data which will pass the condition, but will be immediately discarded + all_candidates = [1] + while len(all_candidates) > 0: + all_candidates = list(batch_query().limit(batch_size)) + shuffle(all_candidates) + num_selections = max(1, _num_checks(len(all_candidates), collision_chance)) + logger.debug('Found %s/%s matching entries, processing %s', len(all_candidates), batch_size, + num_selections) + candidates = all_candidates[0:num_selections] + for candidate in candidates: + yield candidate diff --git a/util/migrate/backfill_content_checksums.py b/util/migrate/backfill_content_checksums.py new file mode 100644 index 000000000..645b5539e --- /dev/null +++ b/util/migrate/backfill_content_checksums.py @@ -0,0 +1,108 @@ +import logging + +from peewee import JOIN_LEFT_OUTER + +from peewee import (CharField, BigIntegerField, BooleanField, ForeignKeyField, DateTimeField, + TextField) + +from data.database import BaseModel, db, db_for_update, CloseForLongOperation +from app import app, storage +from digest import checksums +from util.migrate import yield_random_entries + + +logger = logging.getLogger(__name__) + + +class Repository(BaseModel): + pass + + +# Vendor the information from tables we will be writing to at the time of this migration +class ImageStorage(BaseModel): + uuid = CharField(index=True, unique=True) + checksum = CharField(null=True) + image_size = BigIntegerField(null=True) + uncompressed_size = BigIntegerField(null=True) + uploading = BooleanField(default=True, null=True) + cas_path = BooleanField(default=True) + content_checksum = CharField(null=True, index=True) + + +class Image(BaseModel): + docker_image_id = CharField(index=True) + repository = ForeignKeyField(Repository) + ancestors = CharField(index=True, default='/', max_length=64535, null=True) + storage = ForeignKeyField(ImageStorage, index=True, null=True) + created = DateTimeField(null=True) + comment = TextField(null=True) + command = TextField(null=True) + aggregate_size = BigIntegerField(null=True) + v1_json_metadata = TextField(null=True) + v1_checksum = CharField(null=True) + + +class ImageStorageLocation(BaseModel): + name = CharField(unique=True, index=True) + + +class ImageStoragePlacement(BaseModel): + storage = ForeignKeyField(ImageStorage) + location = ForeignKeyField(ImageStorageLocation) + + + +def _get_image_storage_locations(storage_id): + placements_query = (ImageStoragePlacement + .select(ImageStoragePlacement, ImageStorageLocation) + .join(ImageStorageLocation) + .switch(ImageStoragePlacement) + .join(ImageStorage, JOIN_LEFT_OUTER) + .where(ImageStorage.id == storage_id)) + + locations = set() + for placement in placements_query: + locations.add(placement.location.name) + + return locations + + +def backfill_content_checksums(): + """ Copies metadata from image storages to their images. """ + logger.debug('Image content checksum backfill: Began execution') + + def batch_query(): + return (ImageStorage + .select(ImageStorage.id, ImageStorage.uuid) + .where(ImageStorage.content_checksum >> None, ImageStorage.uploading == False)) + + for candidate_storage in yield_random_entries(batch_query, 10000, 0.1): + logger.debug('Computing content checksum for storage: %s', candidate_storage.uuid) + + locations = _get_image_storage_locations(candidate_storage.id) + + checksum = None + with CloseForLongOperation(app.config): + try: + # Compute the checksum + layer_path = storage.image_layer_path(candidate_storage.uuid) + with storage.stream_read_file(locations, layer_path) as layer_data_handle: + checksum = 'sha256:{0}'.format(checksums.sha256_file(layer_data_handle)) + except Exception as exc: + logger.warning('Unable to compute checksum for storage: %s', candidate_storage.uuid) + checksum = 'unknown:{0}'.format(exc.__class__.__name__) + + # Now update the ImageStorage with the checksum + with app.config['DB_TRANSACTION_FACTORY'](db): + to_update = db_for_update(ImageStorage.get(ImageStorage.id == candidate_storage.id)) + if to_update.content_checksum is not None: + logger.info('Another worker filled in the checksum: %s', candidate_storage.uuid) + else: + logger.debug('Setting content checksum to %s for %s', checksum, candidate_storage.uuid) + to_update.content_checksum = checksum + to_update.save() + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + # logging.getLogger('peewee').setLevel(logging.CRITICAL) + backfill_content_checksums() diff --git a/util/migrate/backfill_v1_checksums.py b/util/migrate/backfill_v1_checksums.py new file mode 100644 index 000000000..aef7af72e --- /dev/null +++ b/util/migrate/backfill_v1_checksums.py @@ -0,0 +1,70 @@ +import logging + +from peewee import (CharField, BigIntegerField, BooleanField, ForeignKeyField, DateTimeField, + TextField) +from data.database import BaseModel, db, db_for_update +from util.migrate import yield_random_entries +from app import app + + +logger = logging.getLogger(__name__) + + +class Repository(BaseModel): + pass + + +# Vendor the information from tables we will be writing to at the time of this migration +class ImageStorage(BaseModel): + uuid = CharField(index=True, unique=True) + checksum = CharField(null=True) + image_size = BigIntegerField(null=True) + uncompressed_size = BigIntegerField(null=True) + uploading = BooleanField(default=True, null=True) + cas_path = BooleanField(default=True) + content_checksum = CharField(null=True, index=True) + + +class Image(BaseModel): + docker_image_id = CharField(index=True) + repository = ForeignKeyField(Repository) + ancestors = CharField(index=True, default='/', max_length=64535, null=True) + storage = ForeignKeyField(ImageStorage, index=True, null=True) + created = DateTimeField(null=True) + comment = TextField(null=True) + command = TextField(null=True) + aggregate_size = BigIntegerField(null=True) + v1_json_metadata = TextField(null=True) + v1_checksum = CharField(null=True) + + +def backfill_checksums(): + """ Copies checksums from image storages to their images. """ + logger.debug('Image v1 checksum backfill: Began execution') + def batch_query(): + return (Image + .select(Image.id) + .join(ImageStorage) + .where(Image.v1_checksum >> None, ImageStorage.uploading == False, + ~(ImageStorage.checksum >> None))) + + for candidate_image in yield_random_entries(batch_query, 10000, 0.1): + logger.debug('Computing content checksum for storage: %s', candidate_image.id) + + with app.config['DB_TRANSACTION_FACTORY'](db): + try: + image = db_for_update(Image + .select(Image, ImageStorage) + .join(ImageStorage) + .where(Image.id == candidate_image.id)).get() + + image.v1_checksum = image.storage.checksum + image.save() + except Image.DoesNotExist: + pass + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + logging.getLogger('peewee').setLevel(logging.CRITICAL) + backfill_checksums() From fd3f88f48929dba53bbe1033f96fe93c5c97d633 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 6 Nov 2015 15:45:39 -0500 Subject: [PATCH 3/4] Re-enable parent id backfill, use new backfill style --- ...722_backfill_parent_id_and_v1_checksums.py | 2 + util/migrate/backfill_parent_id.py | 52 ++++++++----------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py b/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py index e6d732dcc..2f6772b66 100644 --- a/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py +++ b/data/migrations/versions/22af01f81722_backfill_parent_id_and_v1_checksums.py @@ -11,8 +11,10 @@ revision = '22af01f81722' down_revision = '2827d36939e4' from util.migrate.backfill_v1_checksums import backfill_checksums +from util.migrate.backfill_parent_id import backfill_parent_id def upgrade(tables): + backfill_parent_id() backfill_checksums() def downgrade(tables): diff --git a/util/migrate/backfill_parent_id.py b/util/migrate/backfill_parent_id.py index 0d2540489..2a4e7b091 100644 --- a/util/migrate/backfill_parent_id.py +++ b/util/migrate/backfill_parent_id.py @@ -1,46 +1,38 @@ import logging -from data.database import Image, ImageStorage, db + +from data.database import Image, ImageStorage, db, db_for_update from app import app +from util.migrate import yield_random_entries + logger = logging.getLogger(__name__) + def backfill_parent_id(): logger.setLevel(logging.DEBUG) logger.debug('backfill_parent_id: Starting') logger.debug('backfill_parent_id: This can be a LONG RUNNING OPERATION. Please wait!') - # Check for any images without parent - has_images = bool(list(Image - .select(Image.id) - .join(ImageStorage) - .where(Image.parent >> None, Image.ancestors != '/', ImageStorage.uploading == False) - .limit(1))) + def fetch_batch(): + return (Image + .select(Image.id, Image.ancestors) + .join(ImageStorage) + .where(Image.parent >> None, Image.ancestors != '/', + ImageStorage.uploading == False)) - if not has_images: - logger.debug('backfill_parent_id: No migration needed') - return + for to_backfill in yield_random_entries(fetch_batch, 10000, 0.3): + with app.config['DB_TRANSACTION_FACTORY'](db): + try: + image = db_for_update(Image + .select() + .where(Image.id == to_backfill.id)).get() + image.parent = to_backfill.ancestors.split('/')[-2] + image.save() + except Image.DoesNotExist: + pass - while True: - # Load the record from the DB. - batch_images_ids = list(Image - .select(Image.id) - .join(ImageStorage) - .where(Image.parent >> None, Image.ancestors != '/', ImageStorage.uploading == False) - .limit(100)) - - if len(batch_images_ids) == 0: - logger.debug('backfill_parent_id: Completed') - return - - for image_id in batch_images_ids: - with app.config['DB_TRANSACTION_FACTORY'](db): - try: - image = Image.select(Image.id, Image.ancestors).where(Image.id == image_id).get() - image.parent = image.ancestors.split('/')[-2] - image.save() - except Image.DoesNotExist: - pass + logger.debug('backfill_parent_id: Completed') if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) From 75f917f592ccacfe2f2092430d63c442bbcf0a56 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 6 Nov 2015 16:08:14 -0500 Subject: [PATCH 4/4] Stop reading the v1 checksums from storage --- endpoints/v1/registry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/endpoints/v1/registry.py b/endpoints/v1/registry.py index 19915363c..72f8843f5 100644 --- a/endpoints/v1/registry.py +++ b/endpoints/v1/registry.py @@ -278,14 +278,14 @@ def put_image_layer(namespace, repository, image_id): except (IOError, checksums.TarError) as exc: logger.debug('put_image_layer: Error when computing tarsum %s', exc) - if repo_image.storage.checksum is None: + if repo_image.v1_checksum is None: # We don't have a checksum stored yet, that's fine skipping the check. # Not removing the mark though, image is not downloadable yet. session['checksum'] = csums session['content_checksum'] = 'sha256:{0}'.format(ch.hexdigest()) return make_response('true', 200) - checksum = repo_image.storage.checksum + checksum = repo_image.v1_checksum # We check if the checksums provided matches one the one we computed if checksum not in csums: