Exemple #1
0
 /// <summary>
 /// Remove the loading value reference from the cache.
 /// </summary>
 /// <param name="key">The key associated with the loading value.</param>
 /// <param name="value_reference">The value reference to be removed from
 /// the cache.</param>
 /// <returns>
 /// <c>true</c> if the value is succesfully removed from the cache;
 /// otherwise, false.
 /// </returns>
 /// <remarks>
 /// If a loading value reference has an active value means that the loading
 /// process has failed and the old valud is still valid, in that case this
 /// method will replace the loading value reference with the old value.
 /// </remarks>
 bool RemoveLoadingValue(string key, LoadingValueReference <T> value_reference)
 {
     lock (mutex_) {
         CacheEntry <T> entry;
         if (cache_provider_.Get(CacheKey(key), out entry))
         {
             IValueReference <T> value = entry.ValueReference;
             if (value == value_reference)
             {
                 // If a loading value is active means that the new value has failed
                 // to load and the old value is still valid.
                 if (value_reference.IsActive)
                 {
                     entry.ValueReference = value_reference.OldValue;
                 }
                 else
                 {
                     cache_provider_.Remove(CacheKey(key));
                 }
                 return(true);
             }
         }
         return(false);
     }
 }
Exemple #2
0
        /// <summary>
        /// Waits uninterruptibly for <paramref name="new_value"/> to be loaded.
        /// </summary>
        /// <param name="key">The key associated with the laoding value.</param>
        /// <param name="loading_value_reference"></param>
        /// <param name="new_value"></param>
        /// <returns></returns>
        T GetUninterruptibly(string key,
                             LoadingValueReference <T> loading_value_reference, IFuture <T> new_value)
        {
            T value = default(T);

            try {
                value = Uninterruptibles.GetUninterruptibly(new_value);

                // Cache loader should never returns null for reference types.
                if (IsNull(value))
                {
                    throw new InvalidCacheLoadException(
                              "CacheLoader returned a null for key " + key + ".");
                }
                // TODO(neylor.silva): Record load success stats.
                StoreLoadedValue(key, loading_value_reference, value);
                return(value);
            } finally {
                if (IsNull(value))
                {
                    // TODO(neylor.silva): Record load exception stats.
                    RemoveLoadingValue(key, loading_value_reference);
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Store the loaded value in cache.
        /// </summary>
        /// <param name="key">
        /// The key associated with the value.
        /// </param>
        /// <param name="old_value_reference">
        /// A <see cref="LoadingValueReference{T}"/> that was used to load the
        /// value.
        /// </param>
        /// <param name="new_value">
        /// The value to be stored in cache.
        /// </param>
        /// <returns>
        /// <c>true</c> if the value was successfully stored in cache; otherwise,
        /// false.
        /// </returns>
        /// <remarks>
        /// This method is called to store the result of the loading computation.
        /// If the value was sucessfully loaded it will be stored in cache using
        /// the given key.
        /// <para>
        /// This method fails to store the value if a value for the given key was
        /// already replaced by another item, while it is loading.
        /// </para>
        /// </remarks>
        bool StoreLoadedValue(string key,
                              LoadingValueReference <T> old_value_reference, T new_value)
        {
            lock (mutex_) {
                long           now = Clock.NanoTime;
                CacheEntry <T> entry;
                if (cache_provider_.Get(CacheKey(key), out entry))
                {
                    IValueReference <T> value_reference = entry.ValueReference;
                    if (old_value_reference == value_reference)
                    {
                        // If the old value is still active notify the caller that we
                        // are replacing it.
                        if (old_value_reference.IsActive)
                        {
                            // TODO(neylor.silva): Notify the caller about the removal cause
                            // of the item from the cache(REPLACED).
                        }
                        SetValue(entry, key, new_value, now);
                        return(true);
                    }

                    // the loaded value was already clobbered.
                    // TODO(neylor.silva): Notify the caller about the removal cause
                    // of the item from the cache(REPLACED).
                    return(false);
                }

                // an entry for the given key does not exist yet, create a new one.
                entry = new CacheEntry <T>(key);
                SetValue(entry, key, new_value, now);
                return(true);
            }
        }
Exemple #4
0
        // At most one of LoadSync/LoadAsync may be called for any given
        // LoadingValueReference.

        T LoadSync(string key, LoadingValueReference <T> loading_value_reference,
                   CacheLoader <T> loader)
        {
            IFuture <T> loading_future = loading_value_reference.LoadFuture(key, loader);

            return(GetUninterruptibly(key, loading_value_reference, loading_future));
        }
Exemple #5
0
        /// <summary>
        /// Creates a new <see cref="LoadingValueReference{T}"/> and inserts it on
        /// the cache by using the <paramref name="key"/>.
        /// </summary>
        /// <param name="key">The key that will be associated with the newly
        /// created <see cref="LoadingValueReference{T}"/>.</param>
        /// <returns>The newly inserted <see cref="LoadingValueReference{T}"/>, or
        /// null if the live value reference is already loading.</returns>
        LoadingValueReference <T> InsertLoadingValueReference(string key)
        {
            lock (mutex_) {
                long now = Clock.NanoTime;

                LoadingValueReference <T> loading_value_reference;

                // look for an existing entry
                CacheEntry <T> entry;
                if (cache_provider_.Get(CacheKey(key), out entry))
                {
                    IValueReference <T> value_reference = entry.ValueReference;
                    if (value_reference.IsLoading)
                    {
                        // refresh is a no-op if loading is pending.
                        return(null);
                    }

                    // continue returning old value while loading
                    loading_value_reference = new LoadingValueReference <T>(value_reference);
                    entry.ValueReference    = loading_value_reference;
                    return(loading_value_reference);
                }

                loading_value_reference = new LoadingValueReference <T>();
                entry = new CacheEntry <T>(key);
                entry.ValueReference = loading_value_reference;

                // send the entry to the cache provider.
                cache_provider_.Set(CacheKey(key), entry);
                return(loading_value_reference);
            }
        }
Exemple #6
0
        IFuture <T> LoadAsync(string key,
                              LoadingValueReference <T> loading_value_reference, CacheLoader <T> loader)
        {
            IFuture <T> loading_future = loading_value_reference.LoadFuture(key, loader);

            loading_future.AddListener(delegate() {
                try {
                    T new_value = GetUninterruptibly(key, loading_value_reference,
                                                     loading_future);
                    // update loading future for the sake of other pending requests.
                    loading_value_reference.Set(new_value);
                } catch (Exception exception) {
                    MustLogger.ForCurrentProcess.Warn("Exception thrown during refresh",
                                                      exception);
                }
            }, Executors.SameThreadExecutor());
            return(loading_future);
        }
Exemple #7
0
        /// <summary>
        /// Refreshes the value associated with <paramref name="key"/>, unless
        /// another thread is already doing so.
        /// </summary>
        /// <param name="key">
        /// The key associated with the value to refresh.
        /// </param>
        /// <param name="loader">
        /// A <see cref="CacheLoader{T}"/> that is used to refresh the value.
        /// </param>
        /// <param name="value">
        /// The newly refreshed value associated with <paramref name="key"/> if
        /// the value was refreshed inline, or the default value of
        /// <typeparamref name="T"/> if another thread is performing the refresh or
        /// if a error occurs during refresh.
        /// </param>
        /// <returns>
        /// <c>true</c> if the value was refreshed and <c>false</c> if another
        /// thread is performing the refresh or if a error has been occured during
        /// the refresh.
        /// </returns>
        bool Refresh(string key, CacheLoader <T> loader, out T value)
        {
            LoadingValueReference <T> loading_value_reference =
                InsertLoadingValueReference(key);

            value = default(T);
            if (loading_value_reference == null)
            {
                return(false);
            }

            IFuture <T> result = LoadAsync(key, loading_value_reference, loader);

            if (result.IsCompleted)
            {
                try {
                    value = result.Get();
                } catch {
                    // don't let refresh exceptions propagate; error was already logged.
                }
            }
            return(false);
        }
Exemple #8
0
        /// <summary>
        /// Atomically get or loads and get the value for the specified key.
        /// </summary>
        /// <param name="key">
        /// The key associated with the value to get.
        /// </param>
        /// <param name="loader">
        /// A <see cref="CacheLoader{T}"/> that could be used to load the value
        /// for the key <paramref name="key"/>.
        /// </param>
        /// <returns>
        /// A object of type <typeparamref name="T"/> that is associated with the
        /// key <paramref name="key"/>.
        /// </returns>
        T LockedGetOrLoad(string key, CacheLoader <T> loader)
        {
            CacheEntry <T>            entry;
            IValueReference <T>       value_reference         = null;
            LoadingValueReference <T> loading_value_reference = null;
            bool create_new_entry = true;

            lock (mutex_) {
                // re-read the time once inside the lock
                long now = Clock.NanoTime;
                if (cache_provider_.Get(CacheKey(key), out entry))
                {
                    value_reference = entry.ValueReference;
                    if (value_reference.IsLoading)
                    {
                        create_new_entry = false;
                    }
                    else
                    {
                        T value = value_reference.Value;
                        if (IsExpired(entry, now))
                        {
                            // TODO(neylor.silva) Notificate the caller about the
                            // expiration(Reason: EXPIRED).
                        }
                        else
                        {
                            RecordRead(entry, now);
                            // TODO(neylor.sila): Record hits stats.
                            return(value);
                        }

                        // TODO(neylor.silva): Update the cache count(size).
                    }
                }

                // at this point an entry was not found or it is expired.
                if (create_new_entry)
                {
                    loading_value_reference = new LoadingValueReference <T>();
                    if (entry == null)
                    {
                        entry = new CacheEntry <T>(key);
                        entry.ValueReference = loading_value_reference;
                        cache_provider_.Set(CacheKey(key), entry);
                    }
                    else
                    {
                        // entry exists but is expired, lets update it with a new
                        // loading value.
                        entry.ValueReference = loading_value_reference;
                    }
                }
            }

            // at this point an entry associated with the specified key exists
            // in cache, but it is a loading the value.
            if (create_new_entry)
            {
                try {
                    // TODO (neylor.silva): Add a mechanism to detect recursive loads.
                    return(LoadSync(key, loading_value_reference, loader));
                } finally {
                    // TODO (neylor.silva): Record the misses stats.
                }
            }
            else
            {
                // the entry already exists and the loading process is already
                // started. Wait for loading.
                return(WaitForLoadingValue(entry, key, value_reference));
            }
        }