Ejemplo n.º 1
0
        private async Task ValidateSessionAsync(HashSet <FullCacheRecord> expectedRecords, ICache cache, string sessionId, FakeBuild.CasAccessMethod accessMethod)
        {
            if (!ImplementsTrackedSessions || DummySessionName != null)
            {
                return;
            }

            ICacheReadOnlySession readOnlySession = (await cache.CreateReadOnlySessionAsync()).Success();

            // Check that the content is fine
            HashSet <FullCacheRecord> foundRecords = new HashSet <FullCacheRecord>();

            foreach (var strongFingerprintTask in cache.EnumerateSessionStrongFingerprints(DummySessionName ?? sessionId).Success().OutOfOrderTasks())
            {
                StrongFingerprint strongFingerprint = await strongFingerprintTask;
                CasEntries        casEntries        = (await readOnlySession.GetCacheEntryAsync(strongFingerprint)).Success();
                FullCacheRecord   record            = new FullCacheRecord(strongFingerprint, casEntries);
                XAssert.IsTrue(expectedRecords.Contains(record), "Found record that was not expected!");
                foundRecords.Add(record);
            }

            (await readOnlySession.CloseAsync()).Success();

            await FakeBuild.CheckContentsAsync(cache, foundRecords, accessMethod);

            XAssert.AreEqual(expectedRecords.Count, foundRecords.Count);
        }
Ejemplo n.º 2
0
        public void CasEntriesRoundTrip(int casEntryCount, int determinism)
        {
            CasEntries casEntries = RandomHelpers.CreateRandomCasEntries(casEntryCount, m_buildXLDeterminism[determinism]);
            CasEntries roundTrip  = casEntries.ToMemoization().FromMemoization();

            Assert.Equal(casEntries, roundTrip);
        }
Ejemplo n.º 3
0
        public async Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries casEntries, UrgencyHint urgencyHint, Guid activityId)
        {
            Contract.Requires(!IsClosed);
            Contract.Requires(casEntries.IsValid);

            using (var eventing = new PinToCasMultipleActivity(BasicFilesystemCache.EventSource, activityId, this))
            {
                eventing.Start(casEntries, urgencyHint);

                // First, initiate all of the operations
                var taskValues = new Task <Possible <string, Failure> > [casEntries.Count];
                for (int i = 0; i < casEntries.Count; i++)
                {
                    taskValues[i] = PinToCasAsync(casEntries[i], urgencyHint, activityId);
                }

                // Now await them all (since they can run in parallel
                var results = new Possible <string, Failure> [casEntries.Count];
                for (int i = 0; i < casEntries.Count; i++)
                {
                    results[i] = await taskValues[i];
                }

                // All return results are actually traced via the per-hash call of PinToCas
                return(eventing.Returns(results));
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Checks for discrepancies in the CasEntries between the two caches for each StrongFingerprint
        /// </summary>
        private IEnumerable <CacheError> CheckDeterminism()
        {
            var localDictionary  = m_localChecker.AllFullCacheRecords;
            var remoteDictionary = m_remoteChecker.AllFullCacheRecords;

            foreach (var entry in localDictionary)
            {
                Possible <CasEntries, Failure> possibleCasEntries = entry.Value.Result;
                if (!possibleCasEntries.Succeeded)
                {
                    // A failure here indicates that the remote cache has a full cache record that the local cache does not. This is not an error.
                    continue;
                }

                CasEntries casEntries = possibleCasEntries.Result;
                if (casEntries.Determinism.IsDeterministicTool)
                {
                    if (!remoteDictionary[entry.Key].Equals(entry.Value))
                    {
                        yield return(new CacheError(CacheErrorType.DeterminismError, "Tool Determinism Error on StrongFingerprint: " + entry.Key));
                    }
                }
                else if (casEntries.Determinism.Guid.Equals(m_remoteGuid))
                {
                    if (!remoteDictionary[entry.Key].Equals(entry.Value))
                    {
                        yield return(new CacheError(CacheErrorType.DeterminismError, "Cache Determinism Error on StrongFingerprint: " + entry.Key));
                    }
                }
            }
        }
Ejemplo n.º 5
0
        public async Task DeterminismUpgraded(int fromDeterminism, int toDeterminism, bool differentCasEntries)
        {
            string testName    = I($"DeterminismUpgraded{fromDeterminism}x{toDeterminism}{(differentCasEntries ? "Diff" : "Same")}");
            string testCacheId = MakeCacheId(testName);
            ICache cache       = await CreateCacheAsync(testCacheId);

            string        testSessionId = "Session1-" + testCacheId;
            ICacheSession session       = await CreateSessionAsync(cache, testSessionId);

            // We need at least 2 to make "differentCasEntries" work
            PipDefinition[] pips =
            {
                new PipDefinition("PipA", determinism: s_determinism[fromDeterminism]),
                new PipDefinition("PipB", determinism: s_determinism[fromDeterminism])
            };

            var records = (await pips.BuildAsync(session)).ToArray();

            await CloseSessionAsync(session, testSessionId);

            testSessionId = "Session2-" + testCacheId;
            session       = await CreateSessionAsync(cache, testSessionId);

            // What we will do here is AddOrGet() a record with the determinism bit changed.
            for (int i = 0; i < records.Length; i++)
            {
                var record = records[i];

                // This gets the CasEntries we want
                CasEntries newEntries = records[(i + (differentCasEntries ? 1 : 0)) % records.Length].CasEntries;

                // Validate that the entry for the record is what we expect
                var entries = (await session.GetCacheEntryAsync(record.StrongFingerprint)).Success();
                XAssert.AreEqual(s_determinism[fromDeterminism].EffectiveGuid, entries.Determinism.EffectiveGuid);

                // Now pin the CasElement and all of the CasEntries
                (await session.PinToCasAsync(record.StrongFingerprint.CasElement, CancellationToken.None)).Success();
                (await session.PinToCasAsync(newEntries, CancellationToken.None)).Success();

                // Now make a new record
                var newRecord = (await session.AddOrGetAsync(
                                     record.StrongFingerprint.WeakFingerprint,
                                     record.StrongFingerprint.CasElement,
                                     record.StrongFingerprint.HashElement,
                                     new CasEntries(newEntries, s_determinism[toDeterminism]))).Success();

                // The new record should be null since the determinism was upgraded
                XAssert.IsNull(newRecord.Record);

                // Now, we will try to get the same record from the cache to validate
                // the setting of the bit
                entries = (await session.GetCacheEntryAsync(record.StrongFingerprint)).Success();
                XAssert.AreEqual(newEntries, entries);
                XAssert.AreEqual(s_determinism[toDeterminism].EffectiveGuid, entries.Determinism.EffectiveGuid);
            }

            await CloseSessionAsync(session, testSessionId);
            await ShutdownCacheAsync(cache, testCacheId);
        }
Ejemplo n.º 6
0
        private async Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddToCache(ICacheSession session, params string[] thePaths)
        {
            WeakFingerprintHash weak    = FakeStrongFingerprint.CreateWeak(session.CacheId);
            CasHash             casHash = await AddPathSet(session, thePaths);

            Hash simpleHash = FakeStrongFingerprint.CreateHash(session.CacheSessionId);

            return(await session.AddOrGetAsync(weak, casHash, simpleHash, CasEntries.FromCasHashes(casHash)));
        }
Ejemplo n.º 7
0
        /// <inheritdoc />
        public async Task <Possible <CacheEntryPublishResult, Failure> > TryPublishCacheEntryAsync(
            WeakContentFingerprint weakFingerprint,
            ContentHash pathSetHash,
            StrongContentFingerprint strongFingerprint,
            CacheEntry entry,
            CacheEntryPublishMode publishMode = CacheEntryPublishMode.CreateNew,
            PublishCacheEntryOptions options  = default)
        {
            // We can request semantics appropriate for CreateNewOrReplaceExisting via CacheDeterminism.SinglePhaseNonDeterministic
            // Note that conflict-rejections / failures may still occur.
            CacheDeterminism determinism = publishMode == CacheEntryPublishMode.CreateNewOrReplaceExisting
                ? CacheDeterminism.SinglePhaseNonDeterministic
                : default(CacheDeterminism);

            // N.B. this includes the metadata hash.
            CasEntries adaptedHashes = new CasEntries(
                entry.ToArray(h => new CasHash(new global::BuildXL.Cache.Interfaces.Hash(h))),
                determinism);

            Possible <FullCacheRecordWithDeterminism, Failure> maybePublished = await PerformFingerprintCacheOperationAsync(
                () => m_cache.AddOrGetAsync(
                    weak: new WeakFingerprintHash(new Hash(weakFingerprint.Hash)),
                    casElement: new CasHash(new Hash(pathSetHash)),
                    hashElement: new Hash(strongFingerprint.Hash),
                    hashes: adaptedHashes,
                    urgencyHint: options.ShouldPublishAssociatedContent ? UrgencyHint.RegisterAssociatedContent : UrgencyHint.SkipRegisterContent),
                nameof(TryPublishCacheEntryAsync));

            if (maybePublished.Succeeded)
            {
                if (maybePublished.Result.Record == null)
                {
                    // Happy path: Entry accepted without an alternative.
                    return(CacheEntryPublishResult.CreatePublishedResult());
                }
                else
                {
                    // Less happy path: The underlying store has an alternative entry that we need to use instead.
                    Possible <CacheEntry, Failure> maybeConvertedConflictingEntry = TryConvertCasEntriesToCacheEntry(maybePublished.Result.Record.CasEntries, maybePublished.Result.Record.CacheId);
                    if (maybeConvertedConflictingEntry.Succeeded)
                    {
                        return(CacheEntryPublishResult.CreateConflictResult(maybeConvertedConflictingEntry.Result));
                    }
                    else
                    {
                        return(maybeConvertedConflictingEntry.Failure.Annotate(
                                   "The cache returned a conflicting entry (rejecting the proposed entry), but the conflicting entry is invalid."));
                    }
                }
            }
            else
            {
                return(maybePublished.Failure);
            }
        }
Ejemplo n.º 8
0
        public async Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries casEntries, UrgencyHint urgencyHint, Guid activityId)
        {
            Possible <string, Failure>[] retValues = new Possible <string, Failure> [casEntries.Count];

            for (int i = 0; i < casEntries.Count; i++)
            {
                retValues[i] = await PinToCasAsync(casEntries[i], urgencyHint, activityId);
            }

            return(retValues);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Check the fake build via the cache given
        /// </summary>
        /// <param name="cache">The cache to read from (uses a read-only session)</param>
        /// <param name="index">The "index" CasHash</param>
        /// <param name="entries">The CasHash entries that should match the index</param>
        /// <param name="accessMethod">Method (File or stream) for how files are materialized from the cache</param>
        /// <returns>Success if all worked</returns>
        /// <remarks>
        /// This is tied to the FakeBuild where the set of results is
        /// made as the index file which we use in the strong fingerprint
        /// and basically describes the set of entries that should be in the CasEntries
        /// </remarks>
        public static async Task CheckContentsAsync(ICache cache, CasHash index, CasEntries entries, CasAccessMethod accessMethod = CasAccessMethod.Stream)
        {
            Contract.Requires(cache != null);
            Contract.Requires(entries.IsValid);

            ICacheReadOnlySession session = await cache.CreateReadOnlySessionAsync().SuccessAsync();

            await CheckContentsAsync(session, index, entries, accessMethod);

            await session.CloseAsync().SuccessAsync();
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Checks for any errors with the cas element
        /// and with each of the cas entries
        /// </summary>
        /// <param name="casElement">cas element of a strong fingerprint to check</param>
        /// <param name="casEntries">cas entries to check</param>
        /// <param name="errors">Where any cache errors found get stored</param>
        private async Task CheckCas(CasHash casElement, CasEntries casEntries, ConcurrentDictionary <CacheError, int> errors)
        {
            // Check CasElement
            await CheckCasHashAsync(casElement, errors);

            for (int i = 0; i < casEntries.Count(); i++)
            {
                // Check each CasHash
                CasHash casHash = casEntries[i];
                await CheckCasHashAsync(casHash, errors);
            }
        }
Ejemplo n.º 11
0
        public void CasEntriesToMemoization(int casEntryCount, int determinism)
        {
            CasEntries casEntries = RandomHelpers.CreateRandomCasEntries(casEntryCount, m_buildXLDeterminism[determinism]);
            ContentHashListWithDeterminism contentHashListWithDeterminism = casEntries.ToMemoization();

            Assert.Equal(casEntries.Count, contentHashListWithDeterminism.ContentHashList.Hashes.Count);
            for (int i = 0; i < casEntries.Count; i++)
            {
                Assert.Equal(casEntries[i].ToMemoization(), contentHashListWithDeterminism.ContentHashList.Hashes[i]);
            }

            AssertDeterminismEqualEnough(casEntries.Determinism, contentHashListWithDeterminism.Determinism);
        }
        public Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            var callback = PinToCasMultipleAsyncCallback;

            if (callback != null)
            {
                return(callback(hashes, urgencyHint, activityId, m_realSession));
            }
            else
            {
                return(m_realSession.PinToCasAsync(hashes, urgencyHint, activityId));
            }
        }
Ejemplo n.º 13
0
        public async Task NoItemFingerprint()
        {
            const string TestName    = nameof(NoItemFingerprint);
            string       testCacheId = MakeCacheId(TestName);
            ICache       cache       = await CreateCacheAsync(testCacheId);

            // Now for the session (which we base on the cache ID)
            string testSessionId = "Session1-" + testCacheId;

            ICacheSession session = await CreateSessionAsync(cache, testSessionId);

            // Note that we will be making a new fingerprint with a CasHash of NoItem
            // without pre-sending it as NoItem is a special case - it is nothing
            FullCacheRecord record = await FakeBuild.DoPipAsync(session, TestName);

            // We place this in and did not pin the NoItem yet or even send it around
            // Note that this also is doing a zero-length CasEntries
            var strong = new StrongFingerprint(record.StrongFingerprint.WeakFingerprint, CasHash.NoItem, new Hash(FingerprintUtilities.ZeroFingerprint), TestName);
            FullCacheRecordWithDeterminism oldRecord = (await session.AddOrGetAsync(
                                                            strong.WeakFingerprint,
                                                            strong.CasElement,
                                                            strong.HashElement,
                                                            CasEntries.FromCasHashes())).Success("Should work even though I did not pin CasHash.NoItem, instead it failed with {0}");

            XAssert.IsNull(oldRecord.Record, "Should have been the first one like this");

            var result = await session.GetCacheEntryAsync(strong).SuccessAsync();

            XAssert.AreEqual(0, result.Count, "We should have gotten a zero-length CasEntries");

            // We place this in and did not pin the NoItem yet or even send it around
            // Note that this does an array of NoItem CasEntries and use the
            // record.CasElement as the weak fingerprint
            CasHash[] empties = { CasHash.NoItem, CasHash.NoItem, CasHash.NoItem };
            strong    = new StrongFingerprint(new WeakFingerprintHash(strong.CasElement.ToFingerprint()), CasHash.NoItem, new Hash(FingerprintUtilities.ZeroFingerprint), TestName);
            oldRecord = (await session.AddOrGetAsync(
                             strong.WeakFingerprint,
                             CasHash.NoItem,
                             new Hash(FingerprintUtilities.ZeroFingerprint),
                             empties)).Success("Should work even though I did not pin CasHash.NoItem, instead it failed with {0}");

            XAssert.IsNull(oldRecord.Record, "Should have been the first one like this");

            result = await session.GetCacheEntryAsync(strong).SuccessAsync();

            XAssert.AreEqual(empties, result, "We should have gotten the set of empties");

            await CloseSessionAsync(session, testSessionId);

            await ShutdownCacheAsync(cache, testCacheId);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Writes the Start Activity event
        /// </summary>
        public void Start(CasEntries hashes, UrgencyHint urgencyHint)
        {
            Start();

            if (TraceMethodArgumentsEnabled())
            {
                Write(
                    ParameterOptions,
                    new
                {
                    CasEntries  = hashes.ToETWFormat(),
                    UrgencyHint = urgencyHint,
                });
            }
        }
Ejemplo n.º 15
0
        public void ContentHashListWithDeterminismFromMemoization(int contentHashCount, int determinism)
        {
            ContentHashListWithDeterminism contentHashListWithDeterminism = new ContentHashListWithDeterminism(
                ContentHashList.Random(HashingType, contentHashCount),
                m_memoizationDeterminism[determinism]);
            CasEntries casEntries = contentHashListWithDeterminism.FromMemoization();

            Assert.Equal(contentHashListWithDeterminism.ContentHashList.Hashes.Count, casEntries.Count);
            for (int i = 0; i < casEntries.Count; i++)
            {
                Assert.Equal(contentHashListWithDeterminism.ContentHashList.Hashes[i], casEntries[i].ToMemoization());
            }

            AssertDeterminismEqualEnough(casEntries.Determinism, contentHashListWithDeterminism.Determinism);
        }
Ejemplo n.º 16
0
        /// <inheritdoc />
        public async Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            List <ContentHash> contentHashes = hashes.Select(hash => hash.ToContentHash()).ToList();
            IEnumerable <Task <Indexed <PinResult> > > resultSet = await ReadOnlyCacheSession.PinAsync(new Context(Logger), contentHashes, CancellationToken.None);

            var results = new Possible <string, Failure> [contentHashes.Count];

            foreach (Task <Indexed <PinResult> > resultTask in resultSet)
            {
                Indexed <PinResult> individualResult = await resultTask;
                results[individualResult.Index] = individualResult.Item.FromMemoization(hashes[individualResult.Index], CacheId);
            }

            return(results);
        }
        public async Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            using (var eventing = new PinToCasMultipleActivity(CompositingCache.EventSource, activityId, this))
            {
                eventing.Start(hashes, urgencyHint);

                var results = await m_casSession.PinToCasAsync(hashes, urgencyHint, eventing.Id);

                for (int i = 0; i < results.Length; i++)
                {
                    if (results[i].Succeeded)
                    {
                        PinnedToCas.TryAdd(hashes[i], 0);
                    }
                }

                return(eventing.Returns(results));
            }
        }
Ejemplo n.º 18
0
        private static async Task <FullCacheRecord> DoPipAsyncImpl(ICacheSession session,
                                                                   WeakFingerprintHash weak,
                                                                   Hash simpleHash,
                                                                   FakeBuild fake,
                                                                   CacheDeterminism determinism,
                                                                   CasAccessMethod accessMethod)
        {
            foreach (var strongTask in session.EnumerateStrongFingerprints(weak))
            {
                StrongFingerprint possibleHit = await strongTask.SuccessAsync();

                if (fake.OutputListHash.Equals(possibleHit.CasElement))
                {
                    if (simpleHash.Equals(possibleHit.HashElement))
                    {
                        // A cache hit!  Our strong fingerprint matched
                        return(new FullCacheRecord(possibleHit, await session.GetCacheEntryAsync(possibleHit).SuccessAsync()));
                    }
                }
            }

            // A cache miss - add the content to the cache and then
            // add the build.
            CasHash inputList = await AddToCasAsync(fake.OutputList, accessMethod, session).SuccessAsync();

            CasHash[] items = new CasHash[fake.Outputs.Length];
            for (int i = 0; i < items.Length; i++)
            {
                items[i] = await AddToCasAsync(fake.Outputs[i], accessMethod, session).SuccessAsync();
            }

            CasEntries entries = new CasEntries(items, determinism);

            FullCacheRecordWithDeterminism cacheRecord = await session.AddOrGetAsync(weak, inputList, simpleHash, entries).SuccessAsync();

            XAssert.AreEqual(null, cacheRecord.Record);

            // Produce a full cache record manually - such that the CacheId is our own "NewRecordCacheId"
            return(new FullCacheRecord(new StrongFingerprint(weak, inputList, simpleHash, NewRecordCacheId), entries));
        }
Ejemplo n.º 19
0
        /// <summary>
        /// Writes the Start Activity event
        /// </summary>
        public void Start(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint)
        {
            Start();

            if (TraceMethodArgumentsEnabled())
            {
                Write(
                    ParameterOptions,
                    new
                {
                    WeakFingerprintHash = weak,
                    CasHash             = casElement,
                    Hash        = hashElement,
                    CasEntries  = hashes,
                    UrgencyHint = urgencyHint,
                });
            }
        }
Ejemplo n.º 20
0
        public async Task SimpleDummySession()
        {
            const string TestName    = "SimpleSession";
            string       testCacheId = MakeCacheId(TestName);
            ICache       cache       = await CreateCacheAsync(testCacheId);

            // Now for the session (which we base on the cache ID)
            string testSessionId = "Session1-" + testCacheId;

            ICacheSession session = await CreateSessionAsync(cache, testSessionId);

            // Do the default fake build for this test (first time, no cache hit)
            FullCacheRecord built = await FakeBuild.DoPipAsync(session, TestName);

            XAssert.AreEqual(FakeBuild.NewRecordCacheId, built.CacheId, "Should have been a new cache entry!");

            // Now we see if we can get back the items we think we should
            await CloseSessionAsync(session, testSessionId);

            // We need a read only session to get the CasEntries
            ICacheReadOnlySession readOnlySession = (await cache.CreateReadOnlySessionAsync()).Success();

            // Validate that the cache contains a dummy session and it has the one cache record it needs.
            HashSet <FullCacheRecord> found = new HashSet <FullCacheRecord>();

            foreach (var strongFingerprintTask in cache.EnumerateSessionStrongFingerprints(MemoizationStoreAdapterCache.DummySessionName).Success().OutOfOrderTasks())
            {
                StrongFingerprint strongFingerprint = await strongFingerprintTask;
                CasEntries        casEntries        = (await readOnlySession.GetCacheEntryAsync(strongFingerprint)).Success();
                FullCacheRecord   record            = new FullCacheRecord(strongFingerprint, casEntries);

                // If it is not the record we already found...
                if (!found.Contains(record))
                {
                    found.Add(record);
                }

                XAssert.AreEqual(1, found.Count, "There should be only 1 unique record in the session");

                XAssert.AreEqual(built.StrongFingerprint.WeakFingerprint, record.StrongFingerprint.WeakFingerprint);
                XAssert.AreEqual(built.StrongFingerprint.CasElement, record.StrongFingerprint.CasElement);
                XAssert.AreEqual(built.StrongFingerprint.HashElement, record.StrongFingerprint.HashElement);
                XAssert.AreEqual(built.CasEntries.Count, record.CasEntries.Count, "Did not return the same number of items");
                XAssert.IsTrue(record.CasEntries.Equals(built.CasEntries), "Items returned are not the same hash and/or order order");

                XAssert.AreEqual(built, record);

                // We can not check record.CasEntries.IsDeterministic
                // as the cache may have determined that they are deterministic
                // via cache determinism recovery.
            }

            XAssert.AreEqual(1, found.Count, "There should be 1 and only 1 record in the session!");

            await readOnlySession.CloseAsync().SuccessAsync();

            // Check that the cache has the items in it
            await FakeBuild.CheckContentsAsync(cache, built);

            // Now redo the "build" with a cache hit
            testSessionId = "Session2-" + testCacheId;
            session       = await CreateSessionAsync(cache, testSessionId);

            FullCacheRecord rebuilt = await FakeBuild.DoPipAsync(session, TestName);

            XAssert.AreEqual(built, rebuilt, "Should have been the same build!");

            // We make sure we did get it from a cache rather than a manual rebuild.
            XAssert.AreNotEqual(built.CacheId, rebuilt.CacheId, "Should not be the same cache ID");

            await CloseSessionAsync(session, testSessionId);

            readOnlySession = await cache.CreateReadOnlySessionAsync().SuccessAsync();

            // Now that we have done the second build via a cache hit, it should produce the
            // same cache record as before
            foreach (var strongFingerprintTask in cache.EnumerateSessionStrongFingerprints(MemoizationStoreAdapterCache.DummySessionName).Success().OutOfOrderTasks())
            {
                StrongFingerprint strongFingerprint = await strongFingerprintTask;
                CasEntries        casEntries        = (await readOnlySession.GetCacheEntryAsync(strongFingerprint)).Success();
                FullCacheRecord   record            = new FullCacheRecord(strongFingerprint, casEntries);

                XAssert.IsTrue(found.Contains(record), "Second session should produce the same cache record but did not!");
            }

            (await readOnlySession.CloseAsync()).Success();

            await ShutdownCacheAsync(cache, testCacheId);
        }
Ejemplo n.º 21
0
        public Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            var callback = AddOrGetAsyncCallback;

            if (callback != null)
            {
                return(callback(weak, casElement, hashElement, hashes, urgencyHint, activityId, m_realSession));
            }
            else
            {
                return(m_realSession.AddOrGetAsync(weak, casElement, hashElement, hashes, urgencyHint, activityId));
            }
        }
 public Task <Possible <string, Failure>[]> PinToCasAsync(CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
 {
     return(m_session.PinToCasAsync(hashes, urgencyHint, activityId));
 }
Ejemplo n.º 23
0
        public async Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            var check = await CheckInputList(weak, casElement, hashElement, urgencyHint, activityId);

            if (!check.Succeeded)
            {
                return(check.Failure);
            }

            return(await m_session.AddOrGetAsync(weak, casElement, hashElement, hashes, urgencyHint, activityId));
        }
Ejemplo n.º 24
0
        public async Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            var addResult = await CacheSession.AddOrGetContentHashListAsync(
                new Context(Logger),
                new BuildXL.Cache.MemoizationStore.Interfaces.Sessions.StrongFingerprint(
                    weak.ToMemoization(),
                    new Selector(casElement.ToMemoization(), hashElement.RawHash.ToByteArray())),
                hashes.ToMemoization(),
                CancellationToken.None);

            var strong = new StrongFingerprint(weak, casElement, hashElement, CacheId);

            switch (addResult.Code)
            {
            case AddOrGetContentHashListResult.ResultCode.Success:
                SessionEntries?.TryAdd(strong, 1);

                return(addResult.ContentHashListWithDeterminism.ContentHashList == null
                        ? new FullCacheRecordWithDeterminism(addResult.ContentHashListWithDeterminism.Determinism.FromMemoization())
                        : new FullCacheRecordWithDeterminism(new FullCacheRecord(strong, addResult.ContentHashListWithDeterminism.FromMemoization())));

            case AddOrGetContentHashListResult.ResultCode.SinglePhaseMixingError:
                return(new SinglePhaseMixingFailure(CacheId));

            case AddOrGetContentHashListResult.ResultCode.InvalidToolDeterminismError:
                return(new NotDeterministicFailure(
                           CacheId,
                           new FullCacheRecord(strong, addResult.ContentHashListWithDeterminism.FromMemoization()),
                           new FullCacheRecord(strong, hashes)));

            case AddOrGetContentHashListResult.ResultCode.Error:
                return(new CacheFailure(addResult.ErrorMessage));

            default:
                return(new CacheFailure("Unrecognized AddOrGetContentHashListAsync result code: " + addResult.Code + ", error message: " + (addResult.ErrorMessage ?? string.Empty)));
            }
        }
Ejemplo n.º 25
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)));
            }
        }
Ejemplo n.º 26
0
        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))));
            }
        }
Ejemplo n.º 27
0
        public Task <Possible <FullCacheRecordWithDeterminism, Failure> > AddOrGetAsync(WeakFingerprintHash weak, CasHash casElement, BuildXL.Cache.Interfaces.Hash hashElement, CasEntries hashes, UrgencyHint urgencyHint, Guid activityId)
        {
            Contract.Requires(!IsClosed);
            Contract.Requires(hashes.IsValid);
            Contract.Assert(!m_readOnly);

            return(Task.Run(() => AddOrGet(weak, casElement, hashElement, hashes)));
        }
Ejemplo n.º 28
0
        public async Task CasEntriesReplacedOnMissingContent(int fromDeterminism, int toDeterminism, bool differentCasEntries)
        {
            string testName    = I($"ReplacedOnMissingContent{fromDeterminism}x{toDeterminism}{(differentCasEntries ? "Diff" : "Same")}");
            string testCacheId = MakeCacheId(testName);

            ICache cache = await CreateCacheAsync(testCacheId, strictMetadataCasCoupling : false);

            try
            {
                string        testSessionId = "Session1-" + testCacheId;
                ICacheSession session       = await CreateSessionAsync(cache, testSessionId);

                // We need at least 2 to make "differentCasEntries" work
                FullCacheRecord[] records =
                {
                    RandomHelpers.CreateRandomFullCacheRecord(session.CacheId, s_determinism[fromDeterminism]),
                    RandomHelpers.CreateRandomFullCacheRecord(session.CacheId, s_determinism[fromDeterminism])
                };
                foreach (var record in records)
                {
                    var addResult = await session.AddOrGetAsync(
                        record.StrongFingerprint.WeakFingerprint,
                        record.StrongFingerprint.CasElement,
                        record.StrongFingerprint.HashElement,
                        record.CasEntries).SuccessAsync();

                    XAssert.IsNull(addResult.Record);
                }

                await CloseSessionAsync(session, testSessionId);

                testSessionId = "Session2-" + testCacheId;
                session       = await CreateSessionAsync(cache, testSessionId);

                // What we will do here is AddOrGet() a record with the determinism bit changed.
                for (int i = 0; i < records.Length; i++)
                {
                    var record = records[i];

                    var getResult = await session.GetCacheEntryAsync(record.StrongFingerprint).SuccessAsync();

                    XAssert.AreEqual(record.CasEntries.Determinism.EffectiveGuid, getResult.Determinism.EffectiveGuid);

                    // This gets the CasEntries we want
                    var        recordsLength = records.Length;
                    CasEntries newEntries    = records[(i + (differentCasEntries ? 1 : 0)) % recordsLength].CasEntries;

                    // Validate that the entry for the record is what we expect
                    var entries = (await session.GetCacheEntryAsync(record.StrongFingerprint)).Success();
                    XAssert.AreEqual(s_determinism[fromDeterminism].EffectiveGuid, entries.Determinism.EffectiveGuid);

                    // Now make a new record
                    var newRecord = (await session.AddOrGetAsync(
                                         record.StrongFingerprint.WeakFingerprint,
                                         record.StrongFingerprint.CasElement,
                                         record.StrongFingerprint.HashElement,
                                         new CasEntries(newEntries, s_determinism[toDeterminism]))).Success();

                    // The new record should be null because the old value should have
                    // been replaced with the new value in all cases (due to missing content).
                    XAssert.IsNull(newRecord.Record);

                    // Now, we will try to get the same record from the cache to validate the replacement
                    entries = (await session.GetCacheEntryAsync(record.StrongFingerprint)).Success();
                    XAssert.AreEqual(newEntries, entries);
                    XAssert.AreEqual(s_determinism[toDeterminism].EffectiveGuid, entries.Determinism.EffectiveGuid);
                }

                await CloseSessionAsync(session, testSessionId);
            }
            finally
            {
                await ShutdownCacheAsync(cache, testCacheId);
            }
        }
Ejemplo n.º 29
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)))));
                    }
                }
            }
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Check the fake build via the session given
        /// </summary>
        /// <param name="session">Session to use for the check</param>
        /// <param name="index">The "index" CasHash</param>
        /// <param name="entries">The CasHash entries that should match the index</param>
        /// <param name="accessMethod">Method (File or stream) for how files are materialized from the cache</param>
        /// <returns>An Async Task</returns>
        public static async Task CheckContentsAsync(ICacheReadOnlySession session, CasHash index, CasEntries entries, CasAccessMethod accessMethod = CasAccessMethod.Stream)
        {
            string cacheId = await session.PinToCasAsync(index, CancellationToken.None).SuccessAsync("Cannot pin entry {0} to cache {1}", index.ToString(), session.CacheId);

            string[] expected = (await GetStreamAsync(index, accessMethod, session)).Success().Stream.AsString().Split(s_splitLines, StringSplitOptions.RemoveEmptyEntries);

            XAssert.AreEqual(expected.Length, entries.Count, "Counts did not match from cache {0}: {1} != {2}", cacheId, expected.Length, entries.Count);

            for (int i = 0; i < expected.Length; i++)
            {
                string casCacheId = await session.PinToCasAsync(entries[i], CancellationToken.None).SuccessAsync();

                string entry = (await GetStreamAsync(entries[i], accessMethod, session)).Success().Stream.AsString();

                XAssert.AreEqual(expected[i], entry, "CasEntry {0} mismatch from cache {1}:  [{2}] != [{3}]", i, casCacheId, expected[i], entry);
            }
        }