public async Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId) { using (var eventing = new AddOrGetActivity(CompositingCache.EventSource, activityId, this)) { eventing.Start(weak, casElement, hashElement, hashes, urgencyHint); // First, check if all the content has been pinned. if (Cache.StrictMetadataCasCoupling) { List <CasHash> referencedHashes = new List <CasHash>(hashes); referencedHashes.Add(casElement); foreach (CasHash oneHash in referencedHashes) { if (!PinnedToCas.ContainsKey(oneHash)) { return(eventing.StopFailure(new UnpinnedCasEntryFailure(Cache.CacheId, oneHash))); } } } // Then check the determinism bit... var testStrongFingerprint = new StrongFingerprint(weak, casElement, hashElement, Cache.CacheId); var existRecordCheck = await m_metadataSession.GetCacheEntryAsync(testStrongFingerprint, urgencyHint, eventing.Id); if (existRecordCheck.Succeeded) { // There's an existing record, so we need the rules for determinism upgrade / downgrade. Which are complicated by the existance // (or lack there of) of the associated content. // First we'll check for determinism upgrades or other situations that don't require content probes. var existingRecord = existRecordCheck.Result; // If the determinsim is going from no determinism to some determinism OR the record is going from a non-tool // determinism to a tool determinism, replace if ((existingRecord.Determinism.IsNone && hashes.Determinism.IsDeterministic) || (hashes.Determinism.IsDeterministicTool && !existingRecord.Determinism.IsDeterministicTool)) { return(eventing.Returns(await m_metadataSession.AddOrGetAsync(weak, casElement, hashElement, hashes, urgencyHint, eventing.Id))); } // Before trying to probe for files, see if the entries are identical. // If so, tell the caller we stored their record. if (hashes == existingRecord && hashes.Determinism.EffectiveGuid == existingRecord.Determinism.EffectiveGuid) { return(eventing.Returns(new FullCacheRecordWithDeterminism(CacheDeterminism.None))); } // The rest of the determinism moves depend on if the files exist or not. bool foundAllFiles = true; var pinCheck = await m_casSession.PinToCasAsync(existRecordCheck.Result, urgencyHint, eventing.Id); foreach (var oneOutput in pinCheck) { if (!oneOutput.Succeeded) { foundAllFiles = false; } } // Ok, so now with the knowledge of file existance, if we're missing files, allow the call through no matter what. if (!foundAllFiles) { return(eventing.Returns(await m_metadataSession.AddOrGetAsync(weak, casElement, hashElement, hashes, urgencyHint, eventing.Id))); } // If the existing entry is more deterministic than what we're being given or // the records are the same level, unless tool deterministic or single phase. if ((existingRecord.Determinism.IsDeterministic && !hashes.Determinism.IsDeterministic) || (existingRecord.Determinism.IsDeterministicTool && !hashes.Determinism.IsDeterministicTool) || (existingRecord.Determinism.EffectiveGuid == hashes.Determinism.EffectiveGuid && !existingRecord.Determinism.IsDeterministicTool && !existingRecord.Determinism.IsSinglePhaseNonDeterministic)) { // If the records are identical except for determinsim, return null. if (existingRecord == hashes) { return(eventing.Returns(new FullCacheRecordWithDeterminism(CacheDeterminism.None))); } return(eventing.Returns(new FullCacheRecordWithDeterminism(new FullCacheRecord(testStrongFingerprint, existingRecord)))); } // And now the error conditions. // If a tool determinism collection, or an attempt to go from deterministic to not deterministic. if (existingRecord.Determinism.IsDeterministicTool && hashes.Determinism.IsDeterministicTool) { return(eventing.StopFailure(new NotDeterministicFailure(Cache.CacheId, new FullCacheRecord(testStrongFingerprint, existingRecord), new FullCacheRecord(testStrongFingerprint, hashes)))); } } return(eventing.Returns(await m_metadataSession.AddOrGetAsync(weak, casElement, hashElement, hashes, urgencyHint, eventing.Id))); } }
public async Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId) { Contract.Requires(!IsClosed); Contract.Requires(hashes.IsValid); Contract.Assert(!IsReadOnly); using (var counter = m_counters.AddOrGetCounter()) { using (var eventing = new AddOrGetActivity(BasicFilesystemCache.EventSource, activityId, this)) { eventing.Start(weak, casElement, hashElement, hashes, urgencyHint); counter.SetEntriesCount(hashes.Count); // The size of what we are adding (effectively) // We check the Cas entries if we are strict if (StrictMetadataCasCoupling) { // Check that the content is valid. if (!m_pinnedToCas.ContainsKey(casElement)) { counter.Failed(); return(eventing.StopFailure(new UnpinnedCasEntryFailure(CacheId, casElement))); } foreach (CasHash hash in hashes) { if (!m_pinnedToCas.ContainsKey(hash)) { counter.Failed(); return(eventing.StopFailure(new UnpinnedCasEntryFailure(CacheId, hash))); } } } StrongFingerprint strong = new StrongFingerprint(weak, casElement, hashElement, CacheId); // Assume we accepted the Add and there is nothing to return FullCacheRecord result = null; string strongFingerprintName = m_cache.GetStrongFingerprintFilename(strong); try { using (FileStream file = await m_cache.ContendedOpenStreamAsync(strongFingerprintName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) { // The compiler thinks that it is not assigned down below at the end // even though it would be set by the writeEntries being true in that case CasEntries oldCasEntries = hashes; // Assume we will write our new enties to the file. bool writeEntries = true; // If there is some data in the file already, we need to try to read it. if (file.Length > 0) { var possibleOldCasEntries = await m_cache.ReadCacheEntryAsync(file); // Only if it was formatted correctly do we continue to check if // we should replace it. if (possibleOldCasEntries.Succeeded) { oldCasEntries = possibleOldCasEntries.Result; writeEntries = false; // We can only replace if both or neither is SinglePhaseNonDeterministic if (oldCasEntries.Determinism.IsSinglePhaseNonDeterministic != hashes.Determinism.IsSinglePhaseNonDeterministic) { counter.Failed(); return(eventing.StopFailure(new SinglePhaseMixingFailure(CacheId))); } // Should we replace? if (hashes.Determinism.IsSinglePhaseNonDeterministic || (!oldCasEntries.Determinism.IsDeterministicTool && (hashes.Determinism.IsDeterministic && !oldCasEntries.Determinism.Equals(hashes.Determinism)))) { // We are replacing due to determinism counter.Det(); writeEntries = true; } else if (HasMissingContent(oldCasEntries)) { counter.Repair(); writeEntries = true; } else if (oldCasEntries.Determinism.IsDeterministicTool && hashes.Determinism.IsDeterministicTool && !oldCasEntries.Equals(hashes)) { // We have a non-deterministic tool! counter.Failed(); return(eventing.StopFailure(new NotDeterministicFailure(CacheId, new FullCacheRecord(strong, oldCasEntries), new FullCacheRecord(strong, hashes)))); } } } // Are we going to write the entries? if (writeEntries) { // We are writing so the old entries don't count oldCasEntries = hashes; // Write from the front file.SetLength(0); await m_cache.WriteCacheEntryAsync(file, hashes); } else { counter.Dup(); } // If what is in the cache is different than what we are // asking to add, build a FullCacheRecord to return what // is in the cache. if (!oldCasEntries.Equals(hashes)) { counter.Get(); result = new FullCacheRecord(strong, oldCasEntries); } } } catch (Exception e) { counter.Failed(); return(eventing.StopFailure(new StrongFingerprintAccessFailure(m_cache.CacheId, strong, e))); } AddSessionRecord(strong); if (result != null) { return (eventing.Returns( new FullCacheRecordWithDeterminism( new FullCacheRecord( result.StrongFingerprint, result.CasEntries.GetModifiedCasEntriesWithDeterminism( m_cache.IsAuthoritative, m_cache.CacheGuid, CacheDeterminism.NeverExpires))))); } else { return(eventing.Returns(new FullCacheRecordWithDeterminism(hashes.GetFinalDeterminism(m_cache.IsAuthoritative, m_cache.CacheGuid, DateTime.UtcNow.Add(m_cache.TimeToLive))))); } } } }