private Possible <FullCacheRecordWithDeterminism, Failure> AddOrGet(WeakFingerprintHash weak, CasHash casElement, BuildXL.Cache.Interfaces.Hash hashElement, CasEntries hashes) { Contract.Requires(!IsClosed); Contract.Requires(hashes.IsValid); Contract.Assert(!m_readOnly); // We check the Cas entries if we are strict if (StrictMetadataCasCoupling) { // Check that the content is valid. if (!m_pinnedToCas.ContainsKey(casElement)) { return(new UnpinnedCasEntryFailure(CacheId, casElement)); } foreach (CasHash hash in hashes) { if (!m_pinnedToCas.ContainsKey(hash)) { return(new UnpinnedCasEntryFailure(CacheId, hash)); } } } var strongFingerprints = Cache.Fingerprints.GetOrAdd(weak, (key) => new ConcurrentDictionary <StrongFingerprint, FullCacheRecord>()); var record = strongFingerprints.AddOrUpdate( new StrongFingerprint(weak, casElement, hashElement, Cache.CacheId), (strong) => new FullCacheRecord(strong, hashes), (strong, oldRecord) => { // Do no harm here - we will recheck this outside to produce the error that it was a bad attempt if (oldRecord.CasEntries.Determinism.IsSinglePhaseNonDeterministic != hashes.Determinism.IsSinglePhaseNonDeterministic) { return(oldRecord); } // We replace if we are SinglePhaseNonDeterministic *or* // if we are upgrading the determinism. if (hashes.Determinism.IsSinglePhaseNonDeterministic || (!oldRecord.CasEntries.Determinism.IsDeterministicTool && (hashes.Determinism.IsDeterministic && !hashes.Determinism.Equals(oldRecord.CasEntries.Determinism))) || HasMissingContent(oldRecord.CasEntries)) { oldRecord = new FullCacheRecord(strong, hashes); } return(oldRecord); }); if (record.CasEntries.Determinism.IsSinglePhaseNonDeterministic != hashes.Determinism.IsSinglePhaseNonDeterministic) { return(new SinglePhaseMixingFailure(CacheId)); } AddSessionRecord(record); if (record.CasEntries.Equals(hashes)) { record = null; } else { // Check if tool determinism did not make it in - that means a very bad thing happened if (hashes.Determinism.IsDeterministicTool) { return(new NotDeterministicFailure(Cache.CacheId, record, new FullCacheRecord(record.StrongFingerprint, hashes))); } } if (record == null) { return(new FullCacheRecordWithDeterminism(hashes.GetFinalDeterminism(Cache.IsAuthoritative, Cache.CacheGuid, CacheDeterminism.NeverExpires))); } else { return(new FullCacheRecordWithDeterminism(new FullCacheRecord(record.StrongFingerprint, record.CasEntries.GetModifiedCasEntriesWithDeterminism(Cache.IsAuthoritative, Cache.CacheGuid, CacheDeterminism.NeverExpires)))); } }
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))))); } } } }