/// <summary> /// Start building state for a BatchRangeSet. /// call sequence should be: /// StartBuildingBatchRangeSet( prevRs ) /// AddSyncId(Sid) /// ... /// AddSyncId(Sid) /// StartNextTable( tname ) /// AddSyncId(Sid) /// result = FinishBuildingBatchRangeSet() /// /// Note that this implicitly starts the range with the end of /// prevRS+1 /// ***** We don't track the prevRS internally because we want to be able /// ***** to rebuild a rangeset from scratch if we for /// ***** instance change the batching limits /// </summary> /// <param name="prevRS"> /// The prev rangeset that was built. /// </param> public void StartBuildingBatchRangeSet(BatchRangeSet prevRS) { Debug.Assert(_inProgressRS == null); BatchRange prevLast = prevRS.Last; _inProgressRS = new BatchRangeSet(); // check that this rangeset was not built as the last // rangeset for a knowledge. Debug.Assert(prevLast.End != _tableRanges[prevLast.TableName].End); // was the end of the last range usable? if (!prevLast.RangeIsUsable) { Debug.Assert(prevLast.TableName != null); Debug.Assert(prevLast.Start != null); // if the prev range moved to the current table but did // not have any rows then we are starting the table again. // just take the unusable range as our own _inProgressRS.Add(new BatchRange(prevLast)); } else { // We have a started the current table already // So continue from one greater than the last range BatchRange nextRange = new BatchRange(); nextRange.TableName = prevLast.TableName; nextRange.Start = IdPlusOne(_idFormat, prevLast.End); // leave End as null _inProgressRS.Add(nextRange); } }
/// <summary> /// Returns the range set that we are using and resets the in /// progress state. /// </summary> public BatchRangeSet FinishBuildingBatchRangeSet() { Debug.Assert(_inProgressRS != null); // check if the in progress range set is valid if (_inProgressRS.Count == 1 && !_inProgressRS.Last.RangeIsUsable) { // there where no tables finished or SyncIds added to // this range set! throw new DbSyncException("InteralBatchRangeSetError"); } BatchRangeSet retSet = _inProgressRS; _inProgressRS = null; return(retSet); }
// // **** Methods for pulling sorted data // public IEnumerable <SortedBatch> PullSortedBatches() { // start the first batch and range SortedBatch pendingBatch = new SortedBatch(); long sizeOfBatch = 0; // intialize the range set build because we will be pullig // rows now and need to calculate the correct range sets BatchRangeSetBuilder rangeSetBuilder = new BatchRangeSetBuilder(_srcKnowledge.GetSyncIdFormatGroup().ItemIdFormat, _tablesInApplyOrder); rangeSetBuilder.StartBuildingFirstBatchRangeSet(); // for each table in apply order foreach (string tableName in _tablesInApplyOrder) { // start the next table range rangeSetBuilder.StartNextTable(tableName); // if we have a datatable for this name SortedTable curTable; if (_sortedTables.TryGetValue(tableName, out curTable) && curTable._schema != null) { // add the current table to the batch we are working // on DataTable curDataTable = curTable._schema.Clone(); pendingBatch.sortedDataSet.Tables.Add(curDataTable); curDataTable.BeginLoadData(); // if there are no rows in the table just add it to // the current dataset and move on SyncId maxIdInCurrentTable = null; // pull the rows in SyncId order foreach (KeyValuePair <SyncId, SortedRow> kvp in curTable._rows) { long curRowSize = SyncUtil.GetRowSizeForObjectArray(kvp.Value._rowValues); if (curRowSize > (_maxSortedBatchSizeInKB * 1024)) { // Note: This code is modified to throw a more specific exception. // If we end up merging this code with the provider, then the caller has to be tested to // make sure it works with the logic in the provider codebase. throw SyncServiceException.CreateInternalServerError( String.Format(Strings.RowExceedsConfiguredBatchSize, _maxSortedBatchSizeInKB, tableName, curRowSize)); } // fixme: if this row won't fit then return // the current batch if ((sizeOfBatch + curRowSize) > (_maxSortedBatchSizeInKB * 1024)) { // * done loading data curDataTable.EndLoadData(); // * add last sync id in batch if (maxIdInCurrentTable == null) { // we have not added any rows to the // current table so we should create a // dummy id in the current table for // the range maxIdInCurrentTable = rangeSetBuilder.MakeDummyFirstRowID(tableName); } rangeSetBuilder.AddSyncId(tableName, maxIdInCurrentTable); // start a new batch BatchRangeSet curRS = rangeSetBuilder.FinishBuildingBatchRangeSet(); pendingBatch.sortedDataSetKnowledge = curRS.ProjectOnKnowledge(_srcKnowledge); yield return(pendingBatch); // *** tricky // after yielding the current batch we // need to start a new one for the rest of // the rows in this table. // we must reset all the neede state and // this is tricky maxIdInCurrentTable = null; // start a new batch pendingBatch = new SortedBatch(); sizeOfBatch = 0; // start a new range after the current one rangeSetBuilder.StartBuildingBatchRangeSet(curRS); // add the current table to the batch we are working // on curDataTable = curTable._schema.Clone(); pendingBatch.sortedDataSet.Tables.Add(curDataTable); curDataTable.BeginLoadData(); } AddSortedRowToDataTable(curDataTable, kvp.Value); sizeOfBatch += curRowSize; maxIdInCurrentTable = kvp.Key; } curDataTable.EndLoadData(); } } // we should always be working on a batch { Debug.Assert(pendingBatch != null); BatchRangeSet curRS = rangeSetBuilder.FinishLastBatchRangeSet(); pendingBatch.sortedDataSetKnowledge = curRS.ProjectOnKnowledge(_srcKnowledge); } yield return(pendingBatch); }
/// <summary> /// Aborts the partially build range set if it exists. /// </summary> public void AbortRangeSet() { _inProgressRS = null; }
/// <summary> /// Start building state for a BatchRangeSet. /// call sequence should be: /// StartBuildingFirstBatchRangeSet( ) /// AddSyncId(Sid) /// ... /// AddSyncId(Sid) /// StartNextTable( tname ) /// AddSyncId(Sid) /// result = FinishBuildingBatchRangeSet() /// Note that this implicetly starts the range with /// </summary> public void StartBuildingFirstBatchRangeSet() { // not valid to start a new range set while one is in progress Debug.Assert(_inProgressRS == null); _inProgressRS = new BatchRangeSet(); }
/// <summary> /// Returns the range set that we are using and resets the in /// progress state. /// </summary> public BatchRangeSet FinishBuildingBatchRangeSet() { Debug.Assert( _inProgressRS != null ); // check if the in progress range set is valid if( _inProgressRS.Count == 1 && !_inProgressRS.Last.RangeIsUsable ) { // there where no tables finished or SyncIds added to // this range set! throw new DbSyncException("InteralBatchRangeSetError"); } BatchRangeSet retSet = _inProgressRS; _inProgressRS = null; return retSet; }
/// <summary> /// Start building state for a BatchRangeSet. /// call sequence should be: /// StartBuildingFirstBatchRangeSet( ) /// AddSyncId(Sid) /// ... /// AddSyncId(Sid) /// StartNextTable( tname ) /// AddSyncId(Sid) /// result = FinishBuildingBatchRangeSet() /// Note that this implicetly starts the range with /// </summary> public void StartBuildingFirstBatchRangeSet() { // not valid to start a new range set while one is in progress Debug.Assert( _inProgressRS == null ); _inProgressRS = new BatchRangeSet(); }
/// <summary> /// Start building state for a BatchRangeSet. /// call sequence should be: /// StartBuildingBatchRangeSet( prevRs ) /// AddSyncId(Sid) /// ... /// AddSyncId(Sid) /// StartNextTable( tname ) /// AddSyncId(Sid) /// result = FinishBuildingBatchRangeSet() /// /// Note that this implicitly starts the range with the end of /// prevRS+1 /// ***** We don't track the prevRS internally because we want to be able /// ***** to rebuild a rangeset from scratch if we for /// ***** instance change the batching limits /// </summary> /// <param name="prevRS"> /// The prev rangeset that was built. /// </param> public void StartBuildingBatchRangeSet( BatchRangeSet prevRS ) { Debug.Assert( _inProgressRS == null ); BatchRange prevLast = prevRS.Last; _inProgressRS = new BatchRangeSet(); // check that this rangeset was not built as the last // rangeset for a knowledge. Debug.Assert( prevLast.End != _tableRanges[prevLast.TableName].End ); // was the end of the last range usable? if( !prevLast.RangeIsUsable ) { Debug.Assert( prevLast.TableName != null ); Debug.Assert( prevLast.Start != null ); // if the prev range moved to the current table but did // not have any rows then we are starting the table again. // just take the unusable range as our own _inProgressRS.Add(new BatchRange(prevLast)); } else { // We have a started the current table already // So continue from one greater than the last range BatchRange nextRange = new BatchRange(); nextRange.TableName = prevLast.TableName; nextRange.Start = IdPlusOne( _idFormat, prevLast.End ); // leave End as null _inProgressRS.Add( nextRange ); } }