From d70db78f5af2d5f0b101241d7701e560f89aefde Mon Sep 17 00:00:00 2001 From: Sergey Motornyuk Date: Thu, 24 Nov 2016 13:58:04 +0200 Subject: [PATCH] 3245 datastore_active race condition `datastore_create` directly updates database and solr index and this reduces possibility of conflicts inside simultaneous calls --- ckanext/datastore/logic/action.py | 47 ++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/ckanext/datastore/logic/action.py b/ckanext/datastore/logic/action.py index 2baa02fba6b..f2d44c34ab7 100644 --- a/ckanext/datastore/logic/action.py +++ b/ckanext/datastore/logic/action.py @@ -1,9 +1,10 @@ import logging +import json import pylons import sqlalchemy -import ckan.lib.base as base +import ckan.lib.search as search import ckan.lib.navl.dictization_functions import ckan.logic as logic import ckan.plugins as p @@ -147,9 +148,47 @@ def datastore_create(context, data_dict): log.debug( 'Setting datastore_active=True on resource {0}'.format(resource.id) ) - p.toolkit.get_action('resource_patch')( - context, - {'id': data_dict['resource_id'], 'datastore_active': True}) + # issue #3245: race condition + update_dict = {'datastore_active': True} + + # get extras(for entity update) and package_id(for search index update) + res_query = model.Session.query( + model.resource_table.c.extras, + model.resource_table.c.package_id + ).filter( + model.Resource.id == data_dict['resource_id'] + ) + extras, package_id = res_query.one() + + # update extras in database for record and its revision + extras.update(update_dict) + res_query.update({'extras': extras}, synchronize_session=False) + + model.Session.query(model.resource_revision_table).filter( + model.ResourceRevision.id == data_dict['resource_id'], + model.ResourceRevision.current is True + ).update({'extras': extras}, synchronize_session=False) + + model.Session.commit() + + # get package with updated resource from solr + # find changed resource, patch it and reindex package + psi = search.PackageSearchIndex() + solr_query = search.PackageSearchQuery() + q = { + 'q': 'id:"{0}"'.format(package_id), + 'fl': 'data_dict', + 'wt': 'json', + 'fq': 'site_id:"%s"' % config.get('ckan.site_id'), + 'rows': 1 + } + for record in solr_query.run(q)['results']: + solr_data_dict = json.loads(record['data_dict']) + for resource in solr_data_dict['resources']: + if resource['id'] == data_dict['resource_id']: + resource.update(update_dict) + psi.index_package(solr_data_dict) + break result.pop('id', None) result.pop('private', None)