예제 #1
0
        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)));
            }
        }
예제 #2
0
        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)))));
                    }
                }
            }
        }