public void StressTest()
        {
            var helper        = new PrnHelper(new QuadraticResidueHelper());
            var generatedPrns = new HashSet <string>();

            var context = new WeeeContext(userContext, eventDispatcher);

            uint seed       = (uint)GetCurrentSeed(context);
            var  components = new PrnAsComponents(seed);

            // be careful how high you go with this limit or generatedPrns will fill up
            // and your computer will get stuck in page-fault limbo
            const int Limit = int.MaxValue / 100;

            for (int ii = 0; ii < Limit; ii++)
            {
                if (ii % (Limit / 10) == 0)
                {
                    Debug.WriteLine("Done another ten per cent...");
                }

                var resultingPrn = helper.CreateUniqueRandomVersionOfPrn(components);

                Assert.False(generatedPrns.Contains(resultingPrn),
                             string.Format(
                                 "{0} was generated twice but is supposed to be unique for a very large range of seed values",
                                 resultingPrn));

                generatedPrns.Add(resultingPrn);

                seed       = components.ToSeedValue() + 1;
                components = new PrnAsComponents(seed);
            }
        }
        public void PrnFromSeed_PassedZero_RecomputedSeedValueEqualsZero()
        {
            const uint Value = 0;

            PrnAsComponents prnAsComponents = new PrnAsComponents(Value);

            uint seedFromPrn = prnAsComponents.ToSeedValue();

            Assert.Equal(Value, seedFromPrn);
        }
Beispiel #3
0
        public void PrnFromSeed_PassedZero_RecomputedSeedValueEqualsZero()
        {
            const uint Value = 0;

            PrnAsComponents prnAsComponents = new PrnAsComponents(Value);

            uint seedFromPrn = prnAsComponents.ToSeedValue();

            Assert.Equal(Value, seedFromPrn);
        }
        public void PrnFromSeed_PassedValueOutOfRange_RecomputedSeedValueIsInRange()
        {
            const uint Value = 9967;
            const uint NextValueInRange = 65536;

            PrnAsComponents prnAsComponents = new PrnAsComponents(Value);

            uint seedFromPrn = prnAsComponents.ToSeedValue();

            Assert.Equal(NextValueInRange, seedFromPrn);
        }
Beispiel #5
0
        public void PrnFromSeed_PassedValueOutOfRange_RecomputedSeedValueIsInRange()
        {
            const uint Value            = 9967;
            const uint NextValueInRange = 65536;

            PrnAsComponents prnAsComponents = new PrnAsComponents(Value);

            uint seedFromPrn = prnAsComponents.ToSeedValue();

            Assert.Equal(NextValueInRange, seedFromPrn);
        }
        /// <summary>
        /// Generates unique, pseudorandom PRNs with minimal database interaction.
        /// Works by:
        /// a) uniquely mapping each unsigned integer to another pseudorandom unsigned integer
        /// b) uniquely mapping each unsigned integer to a specific PRN
        /// Combining those two mappings, and using a sequential seed, we can obtain pseudorandom PRNs
        /// with assurance that we will not repeat ourselves for a very, very long time.
        /// </summary>
        /// <param name="context">The database context</param>
        /// <param name="numberOfPrnsNeeded">A non-negative integer</param>
        /// <returns></returns>
        public async Task<Queue<string>> ComputePrns(int numberOfPrnsNeeded)
        {
            bool succeeded = false;
            bool retry = false;
            IEnumerable<DbEntityEntry> staleValues = null;
            List<PrnAsComponents> generatedPrns = new List<PrnAsComponents>();
            ExceptionDispatchInfo exceptionDispatchInfo = null;

            var prnHelper = new PrnHelper(new QuadraticResidueHelper());

            try
            {
                succeeded = false;
                retry = false;

                // to avoid concurrency issues, we want to read the latest seed, 'reserve' some PRNs (figuring
                // out the resulting final seed as we go), and write the final seed back as quickly as possible
                uint originalLatestSeed = (uint)context.SystemData.Select(sd => sd.LatestPrnSeed).First();

                uint currentSeed = originalLatestSeed;
                for (int ii = 0; ii < numberOfPrnsNeeded; ii++)
                {
                    var prnFromSeed = new PrnAsComponents(currentSeed + 1);
                    generatedPrns.Add(prnFromSeed);
                    currentSeed = prnFromSeed.ToSeedValue();
                }

                // we write back the next acceptable seed to the database, for next time
                // since there are some mathematical constraints on the acceptable values
                context.SystemData.First().LatestPrnSeed = currentSeed;
                await context.SaveChangesAsync();

                succeeded = true;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                staleValues = ex.Entries;
                retry = true;
            }
            catch (Exception ex)
            {
                // In .NET 4.5 it is not allowed to use "await" within catch blocks; this forces us to put
                // code after the catch block. As a result of that, we don't want to throw un-handled exceptions
                // here, so we capture the dispatch info and throw it at the end of this method.
                exceptionDispatchInfo = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex);
            }

            if (succeeded)
            {
                // now we're done with the fairly time sensitive database read/write,
                // we can 'randomise' the results at our leisure
                return new Queue<string>(generatedPrns.Select(p => prnHelper.CreateUniqueRandomVersionOfPrn(p)));
            }
            else if (retry)
            {
                // If we need to retry, we are probably in a race condition with another thread.
                // To avoid retrying indefinitely, we'll wait a few milliseconds to get out of sync
                // with the other thread.
                await Task.Delay(TimeSpan.FromMilliseconds(new Random().Next(100)));

                // If the database value for [LatestPrnSeed] was updated between the time we fetched the value
                // tried to update it with our new value, we will get a DbConcurrencyException.
                // To handle this we will just call this method again until it succeeds.
                // However, as dependency injection forces us to reuse the same WeeeContext, the SystemData
                // entity will already be attached to the context giving us the same stale value from when it
                // was first fetched.
                // The DbUpdateConcurrencyException gives us the ability to reload this entity from the database.
                foreach (var entry in staleValues)
                {
                    if (entry.Entity is SystemData)
                    {
                        await entry.ReloadAsync();
                    }
                }

                // Now that we have the latest value loaded, we'll try calling this method again.
                return await ComputePrns(numberOfPrnsNeeded);
            }
            else
            {
                // Something else bad happened and it's not possible to fix that here.
                exceptionDispatchInfo.Throw();
                throw new Exception("This will never be thrown.");
            }
        }
        public void StressTest()
        {
            var helper = new PrnHelper(new QuadraticResidueHelper());
            var generatedPrns = new HashSet<string>();

            var context = new WeeeContext(userContext, eventDispatcher);

            uint seed = (uint)GetCurrentSeed(context);
            var components = new PrnAsComponents(seed);

            // be careful how high you go with this limit or generatedPrns will fill up
            // and your computer will get stuck in page-fault limbo
            const int Limit = int.MaxValue / 100;

            for (int ii = 0; ii < Limit; ii++)
            {
                if (ii % (Limit / 10) == 0)
                {
                    Debug.WriteLine("Done another ten per cent...");
                }

                var resultingPrn = helper.CreateUniqueRandomVersionOfPrn(components);

                Assert.False(generatedPrns.Contains(resultingPrn),
                    string.Format(
                    "{0} was generated twice but is supposed to be unique for a very large range of seed values",
                    resultingPrn));

                generatedPrns.Add(resultingPrn);

                seed = components.ToSeedValue() + 1;
                components = new PrnAsComponents(seed);
            }
        }
        /// <summary>
        /// Generates unique, pseudorandom PRNs with minimal database interaction.
        /// Works by:
        /// a) uniquely mapping each unsigned integer to another pseudorandom unsigned integer
        /// b) uniquely mapping each unsigned integer to a specific PRN
        /// Combining those two mappings, and using a sequential seed, we can obtain pseudorandom PRNs
        /// with assurance that we will not repeat ourselves for a very, very long time.
        /// </summary>
        /// <param name="context">The database context</param>
        /// <param name="numberOfPrnsNeeded">A non-negative integer</param>
        /// <returns></returns>
        public async Task <Queue <string> > ComputePrns(int numberOfPrnsNeeded)
        {
            bool succeeded = false;
            bool retry     = false;
            IEnumerable <DbEntityEntry> staleValues           = null;
            List <PrnAsComponents>      generatedPrns         = new List <PrnAsComponents>();
            ExceptionDispatchInfo       exceptionDispatchInfo = null;

            var prnHelper = new PrnHelper(new QuadraticResidueHelper());

            try
            {
                succeeded = false;
                retry     = false;

                // to avoid concurrency issues, we want to read the latest seed, 'reserve' some PRNs (figuring
                // out the resulting final seed as we go), and write the final seed back as quickly as possible
                uint originalLatestSeed = (uint)context.SystemData.Select(sd => sd.LatestPrnSeed).First();

                uint currentSeed = originalLatestSeed;
                for (int ii = 0; ii < numberOfPrnsNeeded; ii++)
                {
                    var prnFromSeed = new PrnAsComponents(currentSeed + 1);
                    generatedPrns.Add(prnFromSeed);
                    currentSeed = prnFromSeed.ToSeedValue();
                }

                // we write back the next acceptable seed to the database, for next time
                // since there are some mathematical constraints on the acceptable values
                context.SystemData.First().LatestPrnSeed = currentSeed;
                await context.SaveChangesAsync();

                succeeded = true;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                staleValues = ex.Entries;
                retry       = true;
            }
            catch (Exception ex)
            {
                // In .NET 4.5 it is not allowed to use "await" within catch blocks; this forces us to put
                // code after the catch block. As a result of that, we don't want to throw un-handled exceptions
                // here, so we capture the dispatch info and throw it at the end of this method.
                exceptionDispatchInfo = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex);
            }

            if (succeeded)
            {
                // now we're done with the fairly time sensitive database read/write,
                // we can 'randomise' the results at our leisure
                return(new Queue <string>(generatedPrns.Select(p => prnHelper.CreateUniqueRandomVersionOfPrn(p))));
            }
            else if (retry)
            {
                // If we need to retry, we are probably in a race condition with another thread.
                // To avoid retrying indefinitely, we'll wait a few milliseconds to get out of sync
                // with the other thread.
                await Task.Delay(TimeSpan.FromMilliseconds(new Random().Next(100)));

                // If the database value for [LatestPrnSeed] was updated between the time we fetched the value
                // tried to update it with our new value, we will get a DbConcurrencyException.
                // To handle this we will just call this method again until it succeeds.
                // However, as dependency injection forces us to reuse the same WeeeContext, the SystemData
                // entity will already be attached to the context giving us the same stale value from when it
                // was first fetched.
                // The DbUpdateConcurrencyException gives us the ability to reload this entity from the database.
                foreach (var entry in staleValues)
                {
                    if (entry.Entity is SystemData)
                    {
                        await entry.ReloadAsync();
                    }
                }

                // Now that we have the latest value loaded, we'll try calling this method again.
                return(await ComputePrns(numberOfPrnsNeeded));
            }
            else
            {
                // Something else bad happened and it's not possible to fix that here.
                exceptionDispatchInfo.Throw();
                throw new Exception("This will never be thrown.");
            }
        }