/// <summary> /// Reads data using only header information. Can be used by ReadDictionary /// so it handles deleted content too. /// </summary> /// <param name="contentLocation">Location of the data item without /proxy</param> /// <param name="contentType">GetMime()</param> /// <param name="eTag">Version number</param> /// <param name="addEntryToSender">These are entries that the sender does not know about</param> /// <param name="getEntryFromSender">This function fills in a list of entries that need to be requested from the sender</param> private ResponseAction ReadDataStub(string contentLocation, string contentType, string eTag, List<int> sentFromList, out DataHeader getEntryFromSender, out SendMemoryToPeer addEntryToSender) { ResponseAction success = ResponseAction.DoNotForward; ETag tag = ReadETag(eTag); DataEntry get = null; getEntryFromSender = null; addEntryToSender = null; // create a stub of the item DataEntry create = new DataEntry(contentLocation, tag, sentFromList); create.subscribed = keysToListen.IsSubscribed(contentLocation); create.SetMime(contentType); // manually erase the value (TODO, don't erase the value) // always default to singleton because we assume that a GET is required to complete the request if (create.type != Data.ValueType.Removed) create.value = DataMissing.Singleton; this.dataLock.EnterUpgradeableReadLock(); try { if (this.data.ContainsKey(contentLocation)) { // update the version number of the stub get = this.data[contentLocation]; } else { this.dataLock.EnterWriteLock(); try { this.data.Add(contentLocation, create); } finally { this.dataLock.ExitWriteLock(); } if (create.subscribed && DataMissing.IsSingleton(create.value)) { // we'll have to wait for the data to arrive on the wire // actually get the data getEntryFromSender = new DataHeader(contentLocation, tag, sentFromList); success = ResponseAction.DoNotForward; } else { success = ResponseAction.ForwardToSuccessor; } } } finally { this.dataLock.ExitUpgradeableReadLock(); } if (get != null) { lock (get) { if (create.subscribed) { ETagCompare compareResult = ETag.CompareETags(tag, get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Same || compareResult == ETagCompare.Conflict) { getEntryFromSender = new DataHeader(create.key, create.GetETag(), sentFromList); success = ResponseAction.DoNotForward; } else //if (compareResult == ETagCompare.SecondIsNewer ) { // i know about something newer than the sender, tell the sender //SendMemoryToPeer mem = new SendMemoryToPeer(get.key,sentFromList); //ResponseHeadStub(HEAD, get.key, GetListOfThisLocalID(), 0, get.GetMime(), get.GetETag(), get.IsEmpty, mem.MemBuffer, false); //addEntryToSender = mem; // well, predecessor already been handled above, so we only need to tell // the others success = ResponseAction.ForwardToAll; } } else { // not subscribed // just record the fact that there is newer data on the wire; cannot resolve conflicts without being a subscriber ETagCompare compareResult = ETag.CompareETags(create.GetETag(), get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Same || compareResult == ETagCompare.Conflict) { get.lastOwnerID = create.lastOwnerID; get.lastOwnerRevision = create.lastOwnerRevision; get.value = DataMissing.Singleton; if (compareResult != ETagCompare.Same) { get.senderPath = create.senderPath; success = ResponseAction.ForwardToSuccessor; } else success = ResponseAction.DoNotForward; } else // if (compareResult == ETagCompare.SecondIsNewer ) { //// i know about something newer than the sender //SendMemoryToPeer mem = new SendMemoryToPeer(get.key,sentFromList); //ResponseHeadStub(HEAD, get.key, GetListOfThisLocalID(), 0, get.GetMime(), get.GetETag(), get.IsEmpty, mem.MemBuffer, false); //addEntryToSender = mem; // tell the others too (already told predecessor above) success = ResponseAction.ForwardToAll; } } } } return success; }
/// <summary> /// reads all sorts of data types /// </summary> /// <param name="contentLocation">location of the data</param> /// <param name="eTag">latest version of data being read</param> /// <param name="contentType"></param> /// <param name="dataOnWire">data read</param> /// <returns></returns> private ResponseAction ReadData(string contentLocation, string eTag, string contentType, List<int> senders, byte[] dataOnWire) { ResponseAction success = ResponseAction.DoNotForward; ETag tag = ReadETag(eTag); // constitute object DataEntry create = new DataEntry(contentLocation, tag, senders); create.subscribed = this.keysToListen.IsSubscribed(contentLocation); create.ReadBytesUsingMime(contentType, dataOnWire); bool upgradeable = true; DataEntry get = null; this.dataLock.EnterUpgradeableReadLock(); try { if (this.data.ContainsKey(contentLocation)) { // update exisitng entry get = this.data[contentLocation]; } if (get == null) { this.dataLock.EnterWriteLock(); try { // don't save the value if not subscribed if (!create.subscribed) { create.value = DataMissing.Singleton; } this.data.Add(contentLocation, create); } finally { this.dataLock.ExitWriteLock(); this.dataLock.ExitUpgradeableReadLock(); upgradeable = false; } if (create.subscribed) { // notify API user this.controller.Notified(new NotificationEventArgs(create, contentLocation, NotificationReason.Add, null)); } // never seen before, thus tell others success = ResponseAction.ForwardToSuccessor; } } finally { if (upgradeable) this.dataLock.ExitUpgradeableReadLock(); } if (get != null) { object oldValue = null; lock (get) { if (create.subscribed) { ETagCompare compareResult = ETag.CompareETags(create.GetETag(), get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Conflict || compareResult == ETagCompare.Same) { oldValue = get.value; if (compareResult == ETagCompare.Conflict) { success = ResponseAction.ForwardToAll; // increment the revision and take ownership create.lastOwnerID = this.local_uid; create.lastOwnerRevision = IncrementRevisionRandomizer(create.lastOwnerRevision); } else if (DataMissing.IsSingleton(oldValue)) { success = ResponseAction.ForwardToSuccessor; } else if (compareResult == ETagCompare.Same) { success = ResponseAction.DoNotForward; } else//first is newer { success = ResponseAction.ForwardToSuccessor; } get.lastOwnerID = create.lastOwnerID; get.lastOwnerRevision = create.lastOwnerRevision; get.type = create.type; get.value = create.value; } else // SecondIsNewer { // return this data to the sender success = ResponseAction.ForwardToAll; } } else { ETagCompare compareResult = ETag.CompareETags(create.GetETag(), get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Conflict || compareResult == ETagCompare.Same) { if (compareResult == ETagCompare.Conflict) { success = ResponseAction.ForwardToAll; } else if (compareResult == ETagCompare.Same) { success = ResponseAction.DoNotForward; } else//first is newer { success = ResponseAction.ForwardToSuccessor; } if (compareResult != ETagCompare.Same) { get.lastOwnerID = create.lastOwnerID; get.lastOwnerRevision = create.lastOwnerRevision; get.type = create.type; get.value = DataMissing.Singleton; get.senderPath = create.senderPath; } System.Diagnostics.Debug.Assert(get.type != Data.ValueType.Removed); } else // second is newer { // return this data to the sender success = ResponseAction.ForwardToAll; } } }//lock // notify API user if (success != ResponseAction.DoNotForward && get.subscribed && !DataMissing.IsSingleton(get.value)) { get.senderPath = create.senderPath; this.controller.Notified(new NotificationEventArgs(get,contentLocation, NotificationReason.Change, oldValue)); } } // else if return success; }