/// <summary> /// Standard and transactional rollback /// </summary> public void Rollback() { try { lock (lock_fs) { //Clearing random buffer if (_randBuf.Count() != 0) { usedBufferSize = 0; _randBuf.Clear(); } //Restoring Rollback records byte[] btWork = null; if (_rollbackCache.Count() > 0) { foreach (var rb in _rollbackCache) { btWork = new byte[rb.Value.l]; btWork = _fsRollback.Read((int)rb.Value.o, btWork.Length); //_fsRollback.Position = rb.Value.o; //_fsRollback.Read(btWork, 0, btWork.Length); _fsData.Write_ByOffset((int)rb.Key, btWork); //_fsData.Position = rb.Key; //_fsData.Write(btWork, 0, btWork.Length); } //NET_Flush(_fsData); //Restoring rhp eofRollback = 0; btWork = eofRollback.To_8_bytes_array_BigEndian(); _fsRollbackHelper.Write_ByOffset(0, btWork); //_fsRollbackHelper.Position = 0; //_fsRollbackHelper.Write(eofRollback.To_8_bytes_array_BigEndian(), 0, 8); //NET_Flush(_fsRollbackHelper); //Clearing rollbackCache _rollbackCache.Clear(); } //we dont move eofData, space can be re-used up to next restart (may be root can have this info in next protocols) //eofData = this._fsData.Length; } } catch (Exception ex) { IsOperable = false; throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.RESTORE_ROLLBACK_DATA_FAILED, this._fileName, ex); } }
/// <summary> /// /// </summary> /// <param name="readActual">if actual overwritten data must be used</param> /// <param name="offset"></param> /// <returns></returns> public byte[] Table_Read(bool useCache, long offset, int count) { byte[] res = null; //if (count == 0) //!!! also not necessary, but can be while testing period under exception // return null; lock (lock_fs) { if (!useCache) { //We read exactly what is already saved, without rollback. //all data must be either in file or in buffer //we must form resulting byte[] //Buffer //Finding first element which is smaller or equal then offset List <long> bufKeys = new List <long>(); if (_randBuf.Count() > 0) { var qkvp = _randBuf.OrderByDescending(r => r.Key).Where(r => r.Key < offset).Take(1).Where(r => (r.Key + r.Value.Length - 1) >= offset); if (qkvp.Count() > 0) { bufKeys.Add(qkvp.FirstOrDefault().Key); } foreach (var kvp in _randBuf.OrderBy(r => r.Key).Where(r => r.Key >= offset && r.Key < (offset + count))) { bufKeys.Add(kvp.Key); } } //reading full byte[] from original file and putting on top keys //We use full length of the file if (offset + count > _fsData.EOF) { res = new byte[_fsData.EOF - offset]; } else { res = new byte[count]; } res = _fsData.Read((int)offset, res.Length); byte[] btWork = null; //putting on top foreach (var bk in bufKeys) { btWork = _randBuf[bk]; bool cut = false; int start = 0; int stop = btWork.Length; if (bk < offset) { cut = true; start = Convert.ToInt32(offset - bk); } if ((offset + count) < (bk + btWork.Length)) { cut = true; stop = Convert.ToInt32(offset + count - bk); } if (cut) { byte[] tmp = new byte[stop - start]; Buffer.BlockCopy(btWork, start, tmp, 0, tmp.Length); btWork = tmp; } Buffer.BlockCopy(btWork, 0, res, (start > 0) ? 0 : Convert.ToInt32(bk - offset), btWork.Length); } } else { //we must use rollback file. //We can read only up to commited file lengh eofData List <long> bufKeys = new List <long>(); if (_rollbackCache.Count() > 0) { var qkvp = _rollbackCache.OrderByDescending(r => r.Key).Where(r => r.Key < offset).Take(1).Where(r => (r.Key + r.Value.l - 1) >= offset); if (qkvp.Count() > 0) { bufKeys.Add(qkvp.FirstOrDefault().Key); } foreach (var kvp in _rollbackCache.OrderBy(r => r.Key).Where(r => r.Key >= offset && r.Key < (offset + count))) { bufKeys.Add(kvp.Key); } } //reading full byte[] from original file and putting on top keys /* * Transaction with minimum 2 tables. T2 is inserted, reference to T2 KVP is taken, then this reference is saved into T1. * Commit(). * Commit calls TransactionalCommit for every table sequentially. First it meets table T1, then T2. * In both tables TransactionalCommit procedures are successfull. * then Commit procedure for each table calls TransactionalCommitIsFinished (this proc will clear rollback refs and moves eofData for every table). * First encounters T1 and only then T2. * ....Somewhere here (between calling T1 and T2 TransactionalCommitIsFinished) starts a parallel thread. * After T1 TransactionalCommitIsFinished our parallel thread P1 reads data from T1, * and gets SelectDirect reference to T2 KVP. Then tries to read from not yet TransactionalCommitIsFinished T2. * and for T2 happens: eofData < offset * * To avoid such specific case we use for calculation this._fsData.Length instead of eofData in case if (eofData < offset && TransactionalCommitIsStarted) * 19.07.2013 10:25 */ //WAS //if (offset + count > eofData) // res = new byte[eofData - offset]; //else // res = new byte[count]; ////// //NOW if (offset + count > eofData) { if (eofData < offset && TransactionalCommitIsStarted) //NOT FINISHED multi-table COMMIT. SelectDirect case { //Probably not finished transaction and SelectDirect case. We return value, //because at this momont all transaction table have successfully gone through TransactionalCommit() procedure. if (offset + count > this._fsData.EOF) { res = new byte[this._fsData.EOF - offset]; } else { res = new byte[count]; } } else { res = new byte[eofData - offset]; } } else { res = new byte[count]; } /////// res = _fsData.Read((int)offset, res.Length); byte[] btWork = null; r rb = null; //putting on top foreach (var bk in bufKeys) { rb = _rollbackCache[bk]; //reading from rollback btWork = new byte[rb.l]; btWork = _fsData.Read((int)rb.o, btWork.Length); bool cut = false; int start = 0; int stop = btWork.Length; if (bk < offset) { cut = true; start = Convert.ToInt32(offset - bk); } if ((offset + count) < (bk + btWork.Length)) { cut = true; stop = Convert.ToInt32(offset + count - bk); } if (cut) { byte[] tmp = new byte[stop - start]; Buffer.BlockCopy(btWork, start, tmp, 0, tmp.Length); btWork = tmp; } Buffer.BlockCopy(btWork, 0, res, (start > 0) ? 0 : Convert.ToInt32(bk - offset), btWork.Length); } } } return(res); }