def add_granule(self, stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' if stream_id in self._bad_coverages: log.info( 'Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error( "Couldn't open coverage: %s", DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) raise CorruptionError(e.message) if not coverage: log.error( 'Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- if rdt[rdt.temporal_parameter] is None: log.warning("Empty granule received") return # Parse the RDT and set hte values in the coverage self.insert_values(coverage, rdt, stream_id) # Force the data to be flushed DatasetManagementService._save_coverage(coverage) self.update_metadata(dataset_id, rdt) try: window = rdt[rdt.temporal_parameter][[0, -1]] window = window.tolist() except (ValueError, IndexError): window = None self.dataset_changed(dataset_id, window)
def delete_dataset(self, agent_instance_id, resource_id): """Deletes dataset and coverage files for all of a device's data products""" res_obj = self.rr.read(resource_id) dpms = DataProductManagementServiceProcessClient(process=self) # Find data products from device id count_ds = 0 dp_objs, _ = self.rr.find_objects(resource_id, PRED.hasOutputProduct, RT.DataProduct, id_only=False) for dp_obj in dp_objs: if dpms.is_persisted(dp_obj._id, headers=self._get_system_actor_headers()): if self.force: log.warn("DataProduct %s '%s' is currently persisted - continuing", dp_obj._id, dp_obj.name) else: raise BadRequest("DataProduct %s '%s' is currently persisted. Use force=True to ignore", dp_obj._id, dp_obj.name) ds_objs, _ = self.rr.find_objects(dp_obj._id, PRED.hasDataset, RT.Dataset, id_only=False) for ds_obj in ds_objs: # Delete coverage cov_path = DatasetManagementService._get_coverage_path(ds_obj._id) if os.path.exists(cov_path): log.info("Removing coverage tree at %s", cov_path) shutil.rmtree(cov_path) else: log.warn("Coverage path does not exist %s" % cov_path) # Delete Dataset and associations self.rr.delete(ds_obj._id) count_ds += 1 log.info("Datasets and coverages deleted for device %s '%s': %s", resource_id, res_obj.name, count_ds)
def test_ingestion_failover(self): stream_id, route, stream_def_id, dataset_id = self.make_simple_dataset( ) self.start_ingestion(stream_id, dataset_id) event = Event() def cb(*args, **kwargs): event.set() sub = EventSubscriber(event_type="ExceptionEvent", callback=cb, origin="stream_exception") sub.start() self.publish_fake_data(stream_id, route) self.wait_until_we_have_enough_granules(dataset_id, 40) file_path = DatasetManagementService._get_coverage_path(dataset_id) master_file = os.path.join(file_path, '%s_master.hdf5' % dataset_id) with open(master_file, 'w') as f: f.write('this will crash HDF') self.publish_hifi(stream_id, route, 5) self.assertTrue(event.wait(10)) sub.stop()
def get_coverage_path(self, dataset_id): pth = DatasetManagementService._get_coverage_path(dataset_id) if not os.path.exists(pth): raise ValueError( 'Coverage with id \'{0}\' does not exist!'.format(dataset_id)) return pth
def dead_man_timeout(self, stream_id, callback, *args, **kwargs): done = False timeout = 2 start = time.time() while not done: try: callback(*args, **kwargs) done = True except: log.exception("An issue with coverage, retrying after a bit") if (time.time() - start) > 3600: # After an hour just give up dataset_id = self.get_dataset(stream_id) log.error( "We're giving up, the coverage needs to be inspected %s", DatasetManagementService._get_coverage_path(dataset_id), ) raise if stream_id in self._coverages: log.info("Popping coverage for stream %s", stream_id) self._coverages.pop(stream_id) gevent.sleep(timeout) if timeout > (60 * 5): timeout = 60 * 5 else: timeout *= 2
def test_ingestion_failover(self): stream_id, route, stream_def_id, dataset_id = self.make_simple_dataset() self.start_ingestion(stream_id, dataset_id) event = Event() def cb(*args, **kwargs): event.set() sub = EventSubscriber(event_type="ExceptionEvent", callback=cb, origin="stream_exception") sub.start() self.publish_fake_data(stream_id, route) self.wait_until_we_have_enough_granules(dataset_id, 40) file_path = DatasetManagementService._get_coverage_path(dataset_id) master_file = os.path.join(file_path, '%s_master.hdf5' % dataset_id) with open(master_file, 'w') as f: f.write('this will crash HDF') self.publish_hifi(stream_id, route, 5) self.assertTrue(event.wait(10)) sub.stop()
def delete_dataset(self, agent_instance_id, resource_id): res_obj = self.rr.read(resource_id) dpms = DataProductManagementServiceProcessClient(process=self) # Find data products from device id count_ds = 0 dp_objs, _ = self.rr.find_objects(resource_id, PRED.hasOutputProduct, RT.DataProduct, id_only=False) for dp_obj in dp_objs: if dpms.is_persisted(dp_obj._id): raise BadRequest("DataProduct %s '%s' is currently persisted", dp_obj._id, dp_obj.name) ds_objs, _ = self.rr.find_objects(dp_obj._id, PRED.hasDataset, RT.Dataset, id_only=False) for ds_obj in ds_objs: # Delete coverage cov_path = DatasetManagementService._get_coverage_path(ds_obj._id) if os.path.exists(cov_path): log.info("Removing coverage tree at %s", cov_path) shutil.rmtree(cov_path) else: raise OSError("Coverage path does not exist %s" % cov_path) # Delete Dataset and associations self.rr.delete(ds_obj._id) count_ds += 1 log.info("Datasets and coverages deleted for device %s '%s': %s", resource_id, res_obj.name, count_ds)
def persist_or_timeout(self, stream_id, rdt): """ retry writing coverage multiple times and eventually time out """ done = False timeout = 2 start = time.time() while not done: try: self.add_granule(stream_id, rdt) done = True except: log.exception('An issue with coverage, retrying after a bit') if (time.time() - start) > MAX_RETRY_TIME: # After an hour just give up dataset_id = self.get_dataset(stream_id) log.error( "We're giving up, the coverage needs to be inspected %s", DatasetManagementService._get_coverage_path( dataset_id)) raise if stream_id in self._coverages: log.info('Popping coverage for stream %s', stream_id) self._coverages.pop(stream_id) gevent.sleep(timeout) if timeout > (60 * 5): timeout = 60 * 5 else: timeout *= 2
def persist_or_timeout(self, stream_id, rdt): ''' A loop that tries to parse and store a granule for up to five minutes, and waits an increasing amount of time each iteration. ''' done = False timeout = 2 start = time.time() while not done: if self.parse_granule(stream_id, rdt, start, done): return # We're all done, everything worked if (time.time() - start) > MAX_RETRY_TIME: # After a while, give up dataset_id = self.get_dataset(stream_id) log.error( "We're giving up, the coverage needs to be inspected %s", DatasetManagementService._get_coverage_path(dataset_id)) raise if stream_id in self._coverages: log.info('Popping coverage for stream %s', stream_id) self._coverages.pop(stream_id) gevent.sleep(timeout) timeout = min(60 * 5, timeout * 2)
def add_granule(self,stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' if stream_id in self._bad_coverages: log.info('Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error("Couldn't open coverage: %s", DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) raise CorruptionError(e.message) if not coverage: log.error('Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- if rdt[rdt.temporal_parameter] is None: log.warning("Empty granule received") return # Parse the RDT and set hte values in the coverage self.insert_values(coverage, rdt, stream_id) # Force the data to be flushed DatasetManagementService._save_coverage(coverage) self.update_metadata(dataset_id, rdt) try: window = rdt[rdt.temporal_parameter][[0,-1]] window = window.tolist() except (ValueError, IndexError): window = None self.dataset_changed(dataset_id, window)
def register_dap_dataset(self, dataset_id, data_product_name=''): coverage_path = DatasetManagementService._get_coverage_path(dataset_id) try: self.add_dataset_to_xml(coverage_path=coverage_path, product_name=data_product_name) self.create_symlink(coverage_path, self.pydap_data_path) except: # We don't re-raise to prevent clients from bombing out... log.exception('Problem registering dataset') log.error('Failed to register dataset for coverage path %s' % coverage_path)
def register_dap_dataset(self, dataset_id, data_product_name=''): coverage_path = DatasetManagementService._get_coverage_path(dataset_id) try: self.add_dataset_to_xml(coverage_path=coverage_path, product_name=data_product_name) self.create_symlink(coverage_path, self.pydap_data_path) except: # We don't re-raise to prevent clients from bombing out... log.exception('Problem registering dataset') log.error('Failed to register dataset for coverage path %s' % coverage_path)
def register_dap_dataset(self, data_product_id): dataset_id = self.container.resource_registry.find_objects(data_product_id, PRED.hasDataset, id_only=True)[0][0] data_product = self.container.resource_registry.read(data_product_id) data_product_name = data_product.name stream_definition = self.container.resource_registry.find_objects(data_product_id, PRED.hasStreamDefinition, id_only=False)[0][0] coverage_path = DatasetManagementService._get_coverage_path(dataset_id) try: self.add_dataset_to_xml(coverage_path=coverage_path, product_id=data_product_id, product_name=data_product_name, available_fields=stream_definition.available_fields) self.create_symlink(coverage_path, self.pydap_data_path) except: # We don't re-raise to prevent clients from bombing out... log.exception('Problem registering dataset') log.error('Failed to register dataset for coverage path %s' % coverage_path)
def register_dap_dataset(self, data_product_id): return self.create_entry(data_product_id) dataset_id = self.container.resource_registry.find_objects( data_product_id, PRED.hasDataset, id_only=True)[0][0] data_product = self.container.resource_registry.read(data_product_id) data_product_name = data_product.name stream_definition = self.container.resource_registry.find_objects( data_product_id, PRED.hasStreamDefinition, id_only=False)[0][0] coverage_path = DatasetManagementService._get_coverage_path(dataset_id) try: self.add_dataset_to_xml( coverage_path=coverage_path, product_id=data_product_id, product_name=data_product_name, available_fields=stream_definition.available_fields) #self.create_symlink(coverage_path, self.pydap_data_path) except: # We don't re-raise to prevent clients from bombing out... log.exception('Problem registering dataset') log.error('Failed to register dataset for coverage path %s' % coverage_path)
def persist_or_timeout(self, stream_id, rdt): ''' A loop that tries to parse and store a granule for up to five minutes, and waits an increasing amount of time each iteration. ''' done = False timeout = 2 start = time.time() while not done: if self.parse_granule(stream_id, rdt, start, done): return # We're all done, everything worked if (time.time() - start) > MAX_RETRY_TIME: # After a while, give up dataset_id = self.get_dataset(stream_id) log.error("We're giving up, the coverage needs to be inspected %s", DatasetManagementService._get_coverage_path(dataset_id)) raise if stream_id in self._coverages: log.info('Popping coverage for stream %s', stream_id) self._coverages.pop(stream_id) gevent.sleep(timeout) timeout = min(60 * 5, timeout * 2)
def add_granule(self,stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' debugging = log.isEnabledFor(DEBUG) timer = Timer() if debugging else None if stream_id in self._bad_coverages: log.info('Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Gap Analysis #-------------------------------------------------------------------------------- gap_found = self.has_gap(rdt.connection_id, rdt.connection_index) if gap_found: log.error('Gap Found! New connection: (%s,%s)\tOld Connection: (%s,%s)', rdt.connection_id, rdt.connection_index, self.connection_id, self.connection_index) self.gap_coverage(stream_id) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error("Couldn't open coverage: %s", DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) raise CorruptionError(e.message) if debugging: path = DatasetManagementService._get_coverage_path(dataset_id) log.debug('%s: add_granule stream %s dataset %s coverage %r file %s', self._id, stream_id, dataset_id, coverage, path) if not coverage: log.error('Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- elements = len(rdt) self.insert_sparse_values(coverage,rdt,stream_id) if debugging: timer.complete_step('checks') # lightweight ops, should be zero self.expand_coverage(coverage, elements, stream_id) if debugging: timer.complete_step('insert') self.insert_values(coverage, rdt, stream_id) if debugging: timer.complete_step('keys') DatasetManagementService._save_coverage(coverage) if debugging: timer.complete_step('save') start_index = coverage.num_timesteps - elements self.dataset_changed(dataset_id,coverage.num_timesteps,(start_index,start_index+elements)) if gap_found: self.splice_coverage(dataset_id, coverage) self.evaluate_qc(rdt, dataset_id) if debugging: timer.complete_step('notify') self._add_timing_stats(timer) self.update_connection_index(rdt.connection_id, rdt.connection_index)
def persist_or_timeout(self, stream_id, rdt): """ retry writing coverage multiple times and eventually time out """ done = False timeout = 2 start = time.time() while not done: try: self.add_granule(stream_id, rdt) done = True except: log.exception('An issue with coverage, retrying after a bit') if (time.time() - start) > MAX_RETRY_TIME: # After an hour just give up dataset_id = self.get_dataset(stream_id) log.error("We're giving up, the coverage needs to be inspected %s", DatasetManagementService._get_coverage_path(dataset_id)) raise if stream_id in self._coverages: log.info('Popping coverage for stream %s', stream_id) self._coverages.pop(stream_id) gevent.sleep(timeout) if timeout > (60 * 5): timeout = 60 * 5 else: timeout *= 2
def add_granule(self, stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' debugging = log.isEnabledFor(DEBUG) timer = Timer() if debugging else None if stream_id in self._bad_coverages: log.info( 'Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Gap Analysis #-------------------------------------------------------------------------------- if not self.ignore_gaps: gap_found = self.has_gap(rdt.connection_id, rdt.connection_index) if gap_found: log.error( 'Gap Found! New connection: (%s,%s)\tOld Connection: (%s,%s)', rdt.connection_id, rdt.connection_index, self.connection_id, self.connection_index) self.gap_coverage(stream_id) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error( "Couldn't open coverage: %s", DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) raise CorruptionError(e.message) if debugging: path = DatasetManagementService._get_coverage_path(dataset_id) log.debug( '%s: add_granule stream %s dataset %s coverage %r file %s', self._id, stream_id, dataset_id, coverage, path) if not coverage: log.error( 'Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- elements = len(rdt) if rdt[rdt.temporal_parameter] is None: elements = 0 self.insert_sparse_values(coverage, rdt, stream_id) if debugging: timer.complete_step('checks') # lightweight ops, should be zero self.expand_coverage(coverage, elements, stream_id) if debugging: timer.complete_step('insert') self.insert_values(coverage, rdt, stream_id) if debugging: timer.complete_step('keys') DatasetManagementService._save_coverage(coverage) if debugging: timer.complete_step('save') start_index = coverage.num_timesteps - elements self.dataset_changed(dataset_id, coverage.num_timesteps, (start_index, start_index + elements)) if not self.ignore_gaps and gap_found: self.splice_coverage(dataset_id, coverage) self.evaluate_qc(rdt, dataset_id) if debugging: timer.complete_step('notify') self._add_timing_stats(timer) self.update_connection_index(rdt.connection_id, rdt.connection_index)
def get_coverage_path(self, dataset_id): pth = DatasetManagementService._get_coverage_path(dataset_id) if not os.path.exists(pth): raise ValueError('Coverage with id \'{0}\' does not exist!'.format(dataset_id)) return pth
def add_granule(self,stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' debugging = log.isEnabledFor(DEBUG) timer = Timer() if debugging else None if stream_id in self._bad_coverages: log.info('Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error("Couldn't open coverage: %s", DatasetManagementService._get_coverage_path(self.get_dataset(stream_id))) raise CorruptionError(e.message) if debugging: path = DatasetManagementService._get_coverage_path(dataset_id) log.debug('%s: add_granule stream %s dataset %s coverage %r file %s', self._id, stream_id, dataset_id, coverage, path) if not coverage: log.error('Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- elements = len(rdt) if debugging: timer.complete_step('checks') # lightweight ops, should be zero try: coverage.insert_timesteps(elements, oob=False) except IOError as e: log.error("Couldn't insert time steps for coverage: %s", DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)), exc_info=True) try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) if debugging: timer.complete_step('insert') start_index = coverage.num_timesteps - elements for k,v in rdt.iteritems(): slice_ = slice(start_index, None) try: coverage.set_parameter_values(param_name=k, tdoa=slice_, value=v) except IOError as e: log.error("Couldn't insert values for coverage: %s", DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)), exc_info=True) try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) if 'ingestion_timestamp' in coverage.list_parameters(): t_now = time.time() ntp_time = TimeUtils.ts_to_units(coverage.get_parameter_context('ingestion_timestamp').uom, t_now) coverage.set_parameter_values(param_name='ingestion_timestamp', tdoa=slice_, value=ntp_time) if debugging: timer.complete_step('keys') DatasetManagementService._save_coverage(coverage) if debugging: timer.complete_step('save') self.dataset_changed(dataset_id,coverage.num_timesteps,(start_index,start_index+elements)) if debugging: timer.complete_step('notify') self._add_timing_stats(timer)
def add_granule(self, stream_id, granule): """ Appends the granule's data to the coverage and persists it. """ if stream_id in self._bad_coverages: log.info( "Message attempting to be inserted into bad coverage: %s" % DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)) ) # -------------------------------------------------------------------------------- # Coverage determiniation and appending # -------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error("No dataset could be determined on this stream: %s", stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error( "Couldn't open coverage: %s" % DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)) ) raise CorruptionError(e.message) if not coverage: log.error("Could not persist coverage from granule, coverage is None") return # -------------------------------------------------------------------------------- # Actual persistence # -------------------------------------------------------------------------------- rdt = granule elements = len(rdt) if not elements: return try: coverage.insert_timesteps(elements, oob=False) except IOError as e: log.error( "Couldn't insert time steps for coverage: %s" % DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)) ) log.exception("IOError") try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) start_index = coverage.num_timesteps - elements for k, v in rdt.iteritems(): slice_ = slice(start_index, None) try: coverage.set_parameter_values(param_name=k, tdoa=slice_, value=v) except IOError as e: log.error( "Couldn't insert values for coverage: %s" % DatasetManagementService._get_coverage_path(self.get_dataset(stream_id)) ) log.exception("IOError") try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) DatasetManagementService._save_coverage(coverage) # coverage.flush() self.dataset_changed(dataset_id, coverage.num_timesteps, (start_index, start_index + elements))
def add_granule(self, stream_id, rdt): ''' Appends the granule's data to the coverage and persists it. ''' debugging = log.isEnabledFor(DEBUG) timer = Timer() if debugging else None if stream_id in self._bad_coverages: log.info( 'Message attempting to be inserted into bad coverage: %s', DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) #-------------------------------------------------------------------------------- # Coverage determiniation and appending #-------------------------------------------------------------------------------- dataset_id = self.get_dataset(stream_id) if not dataset_id: log.error('No dataset could be determined on this stream: %s', stream_id) return try: coverage = self.get_coverage(stream_id) except IOError as e: log.error( "Couldn't open coverage: %s", DatasetManagementService._get_coverage_path( self.get_dataset(stream_id))) raise CorruptionError(e.message) if debugging: path = DatasetManagementService._get_coverage_path(dataset_id) log.debug( '%s: add_granule stream %s dataset %s coverage %r file %s', self._id, stream_id, dataset_id, coverage, path) if not coverage: log.error( 'Could not persist coverage from granule, coverage is None') return #-------------------------------------------------------------------------------- # Actual persistence #-------------------------------------------------------------------------------- elements = len(rdt) if debugging: timer.complete_step('checks') # lightweight ops, should be zero try: coverage.insert_timesteps(elements, oob=False) except IOError as e: log.error("Couldn't insert time steps for coverage: %s", DatasetManagementService._get_coverage_path( self.get_dataset(stream_id)), exc_info=True) try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) if debugging: timer.complete_step('insert') start_index = coverage.num_timesteps - elements for k, v in rdt.iteritems(): slice_ = slice(start_index, None) try: coverage.set_parameter_values(param_name=k, tdoa=slice_, value=v) except IOError as e: log.error("Couldn't insert values for coverage: %s", DatasetManagementService._get_coverage_path( self.get_dataset(stream_id)), exc_info=True) try: coverage.close() finally: self._bad_coverages[stream_id] = 1 raise CorruptionError(e.message) if 'ingestion_timestamp' in coverage.list_parameters(): t_now = time.time() ntp_time = TimeUtils.ts_to_units( coverage.get_parameter_context('ingestion_timestamp').uom, t_now) coverage.set_parameter_values(param_name='ingestion_timestamp', tdoa=slice_, value=ntp_time) if debugging: timer.complete_step('keys') DatasetManagementService._save_coverage(coverage) if debugging: timer.complete_step('save') self.dataset_changed(dataset_id, coverage.num_timesteps, (start_index, start_index + elements)) if debugging: timer.complete_step('notify') self._add_timing_stats(timer)