/// <summary>
        /// </summary>
        /// <param name="cancelToken"></param>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        public async Task <MultidimensionalPointResult <TPoint> > TryGetOrCreatePointAsync(CancellationToken cancelToken, params string[] coordinates)
        {
            string pointMoniker = GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = _points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            if (_totalPointsCount >= _totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            await _pointCreationLock.WaitAsync(cancelToken).ConfigureAwait(continueOnCapturedContext: false);

            try
            {
                MultidimensionalPointResult <TPoint> result = TryCreatePoint(coordinates, pointMoniker);
                return(result);
            }
            finally
            {
                _pointCreationLock.Release();
            }
        }
        /// <summary>
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        public MultidimensionalPointResult <TPoint> TryGetOrCreatePoint(params string[] coordinates)
        {
            string pointMoniker = GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = _points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            if (_totalPointsCount >= _totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            _pointCreationLock.Wait();
            try
            {
                MultidimensionalPointResult <TPoint> result = TryCreatePoint(coordinates, pointMoniker);
                return(result);
            }
            finally
            {
                _pointCreationLock.Release();
            }
        }
        private void CtorTestImplementation(
            MultidimensionalCube <string, int> cube, ref int[] subdimensionsCountLimits,
            ref int factoryCallsCount,
            ref object[] lastFactoryCall,
            string suffix,
            int point,
            int totalPointsCountLimit)
        {
            Assert.IsNotNull(cube);
            Assert.AreEqual(subdimensionsCountLimits.Length, cube.DimensionsCount);
            for (int d = 0; d < subdimensionsCountLimits.Length; d++)
            {
                int limit = subdimensionsCountLimits[d];
                Assert.AreEqual(limit, cube.GetSubdimensionsCountLimit(d));
            }

            Assert.AreEqual(totalPointsCountLimit, cube.TotalPointsCountLimit);

            string[] newPointVector = new string[] { $"A{suffix}", $"B{suffix}", $"C{suffix}", $"D{suffix}" };
            MultidimensionalPointResult <int> result = cube.TryGetOrCreatePoint(newPointVector);

            Assert.IsTrue(result.IsSuccess);
            Assert.AreEqual(point, result.Point);

            Util.AssertAreEqual(newPointVector, lastFactoryCall);
        }
Ejemplo n.º 4
0
        public MultidimensionalPointResult <TPoint> TryGetVector(TDimensionValue[] coordinates)
        {
            Util.ValidateNotNull(coordinates, nameof(coordinates));

            if (coordinates.Length != _ownerCube.DimensionsCount)
            {
                throw new ArgumentException(
                          $"The specified {nameof(coordinates)}-vector has {coordinates.Length} dimensions."
                          + $" However {nameof(_ownerCube)} has {_ownerCube.DimensionsCount} dimensions.",
                          nameof(coordinates));
            }

            MultidimensionalPointResult <TPoint> result = this.TryGetOrAddVectorInternal(coordinates, currentDim: 0, createIfNotExists: false);

            return(result);
        }
        /// <summary>
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        public MultidimensionalPointResult <TPoint> TryGetPoint(params string[] coordinates)
        {
            string pointMoniker = GetPointMoniker(coordinates);

            TPoint point;
            bool   hasPoint = _points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }
            else
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_PointDoesNotExistCreationNotRequested, -1);
                return(result);
            }
        }
Ejemplo n.º 6
0
        private MultidimensionalPointResult <TPoint> TryGetOrAddVectorInternal(TDimensionValue[] coordinates, int currentDim, bool createIfNotExists)
        {
            TDimensionValue subElementKey = coordinates[currentDim];

            // Try and get the referenced element:
            object subElement;
            bool   subElementExists = _elements.TryGetValue(subElementKey, out subElement);

            // If the referenced element exists, we can simply proceed:
            if (subElementExists)
            {
                if (_isLastDimensionLevel)
                {
                    var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, (TPoint)subElement);
                    return(result);
                }
                else
                {
                    MultidimensionalCubeDimension <TDimensionValue, TPoint> subDim = (MultidimensionalCubeDimension <TDimensionValue, TPoint>)subElement;
                    MultidimensionalPointResult <TPoint> result = subDim.TryGetOrAddVectorInternal(coordinates, currentDim + 1, createIfNotExists);
                    return(result);
                }
            }
            else // so - subElement does NOT exist:
            {
                // If we are not to create new elements, we are done:
                if (!createIfNotExists)
                {
                    var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_PointDoesNotExistCreationNotRequested, currentDim);
                    return(result);
                }
                else
                {
                    MultidimensionalPointResult <TPoint> result = _isLastDimensionLevel
                                                        ? this.TryAddPoint(coordinates, currentDim)
                                                        : this.TryAddSubvector(coordinates, currentDim);
                    return(result);
                }
            }
        }
Ejemplo n.º 7
0
        private MultidimensionalPointResult <TPoint> TryAddSubvector(TDimensionValue[] coordinates, int currentDim)
        {
            // Note the comment near the top if TryAddPoint(..) about the applied minimal locking strategy.

            // Check if we reached the dimensions count limit. If we did, we give up. Otherwise we start tracking whether we need to undo the increment later:
            if (!this.TryIncSubdimensionsCount())
            {
                return(new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_SubdimensionsCountLimitReached, currentDim));
            }

            bool mustRestoreSubdimensionsCount = true;

            try
            {
                TDimensionValue subElementKey = coordinates[currentDim];

                // Do a soft-check to see if we reached the total points limit. If we do, there is no need to bother:
                // (We will do a hard check and pre-booking later when we actually about to create the point.)
                if (_ownerCube.TotalPointsCount >= _ownerCube.TotalPointsCountLimit)
                {
                    return(new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, failureCoordinateIndex: -1));
                }

                // We are not at the last level. Create the subdimension. Note, we are not under lock, so someone might be creating the same dimention concurrently:
                int  nextDim = currentDim + 1;
                bool isLastDimensionLevel = (nextDim == coordinates.Length - 1);
                var  newSubDim            = new MultidimensionalCubeDimension <TDimensionValue, TPoint>(
                    _ownerCube,
                    _ownerCube.GetSubdimensionsCountLimit(nextDim),
                    isLastDimensionLevel);
                MultidimensionalPointResult <TPoint> newSubDimResult = newSubDim.TryGetOrAddVectorInternal(coordinates, nextDim, createIfNotExists: true);

                // Becasue we have not yet inserted newSubDim into _elements, any operations on newSubDim are not under concurrency.
                // There are no point-vectors yet pointing to the sub-space of newSubDim, so no DimensionValuesCountLimit can be reached.
                // So, hasNewPoint can be false only if TotalPointsCountLimit was reached. We just bail out:
                if (!newSubDimResult.IsSuccess)
                {
                    return(newSubDimResult);
                }

                // The new point has been created and we need to add its sub-space to the list.
                // However, there is a race on someone calling GetOrAddVector(..) with the same coordinates. So newSubDim may or may not already be in the list.
                bool couldInsert = _elements.TryAdd(subElementKey, newSubDim);

                if (couldInsert)
                {
                    // Success. We created and inserted a new sub-space:
                    mustRestoreSubdimensionsCount = false;
                    return(newSubDimResult);
                }
                else
                {
                    // The point was already in the list. It's that other point (created by the race winner) that we want.
                    // We need to discard the sub-space and the point we just created: Decrement total points count and Decrement the dimension value count.
                    // After that we just call TryGetOrAddVectorInternal(..) again on the same recursion level.
                    _ownerCube.DecTotalPointsCount();
                }
            }
            finally
            {
                if (mustRestoreSubdimensionsCount)
                {
                    this.DecSubdimensionsCount();
                }
            }

            MultidimensionalPointResult <TPoint> retryResult = this.TryGetOrAddVectorInternal(coordinates, currentDim, createIfNotExists: true);

            return(retryResult);
        }
        private MultidimensionalPointResult <TPoint> TryCreatePoint(string[] coordinates, string pointMoniker)
        {
            // We already have tried getting the existng point and failed.
            // We also checked that _totalPointsCountLimit was not reached.
            // Lastly, we took a lock.
            // Now we can begin the slow path.

            // First, we need to try retrieving the point again, now under the lock:
            TPoint point;
            bool   hasPoint = _points.TryGetValue(pointMoniker, out point);

            if (hasPoint)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_ExistingPointRetrieved, point);
                return(result);
            }

            // Then, check total count again now that we are under lock:
            if (_totalPointsCount >= _totalPointsCountLimit)
            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_TotalPointsCountLimitReached, -1);
                return(result);
            }

            // Examine each dimension and see if it reached values count limit. If not, track the new value:

            int      reachedValsLimitDim = -1;
            BitArray valueAddedToDims    = new BitArray(length: coordinates.Length, defaultValue: false);

            for (int i = 0; i < coordinates.Length; i++)
            {
                HashSet <string> dimVals       = _dimensionValues[i];
                string           coordinateVal = coordinates[i];

                if ((dimVals.Count >= _dimensionValuesCountLimits[i]) && (false == dimVals.Contains(coordinateVal)))
                {
                    reachedValsLimitDim = i;
                    break;
                }

                bool added = dimVals.Add(coordinates[i]);
                valueAddedToDims.Set(i, added);
            }

            // We hit the _dimensionValuesCountLimits at some dimension.
            // Remove what we just added to dim value sets and give up.

            if (reachedValsLimitDim != -1)
            {
                for (int i = 0; i <= reachedValsLimitDim; i++)
                {
                    if (valueAddedToDims.Get(i))
                    {
                        _dimensionValues[i].Remove(coordinates[i]);
                    }
                }

                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Failure_SubdimensionsCountLimitReached, reachedValsLimitDim);
                return(result);
            }

            // Create new point:

            try
            {
                point = _pointsFactory(coordinates);
            }
            catch (Exception ex)
            {
                // User code in _pointsFactory may throw. In that case we need to clean up from the added value containers:
                for (int i = 0; i <= reachedValsLimitDim; i++)
                {
                    if (valueAddedToDims.Get(i))
                    {
                        _dimensionValues[i].Remove(coordinates[i]);
                    }
                }

                ExceptionDispatchInfo.Capture(ex).Throw();
                throw;  // This line will never be reached
            }

            {
                bool added = _points.TryAdd(pointMoniker, point);
                if (false == added)
                {
                    throw new InvalidOperationException($"Internal Metrics SDK bug. Please report this! (pointMoniker: {pointMoniker})");
                }
            }

            // Inc total points coint.
            _totalPointsCount++;

            {
                var result = new MultidimensionalPointResult <TPoint>(MultidimensionalPointResultCodes.Success_NewPointCreated, point);
                return(result);
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        public MultidimensionalPointResult <TPoint> TryGetPoint(params TDimensionValue[] coordinates)
        {
            MultidimensionalPointResult <TPoint> result = _points.TryGetVector(coordinates);

            return(result);
        }