public IFuture <T> LoadFuture(string key, CacheLoader <T> loader)
        {
            stopwatch_.Start();
            T previous_value = old_value_.Value;

            try {
                // If the T is a value type and its value is default(T) it will never
                // be reloaded and always loaded from the cache loader. We could
                // make the parameter T nullable, but this makes the code much more
                // complicated and we do not want that.
                if (typeof(T).IsValueType || (object)previous_value == null)
                {
                    T new_value = loader.Load(key);
                    return(Set(new_value) ? this : Futures.ImmediateFuture(new_value));
                }
                else
                {
                    IFuture <T> new_value_future = loader.Reload(key, previous_value);
                    return(new_value_future ?? Futures.ImmediateFuture(default(T)));
                }
            } catch (Exception exception) {
                return(SetException(exception)
          ? this
          : Futures.ImmediateFailedFuture <T>(exception));
            }
        }
Example #2
0
        public void ShouldThrowExceptionWhenCacheLoaderReturnsNull()
        {
            CacheBuilder <string>     ref_cache_builder = new CacheBuilder <string>();
            LoadingCacheMock <string> ref_cache         =
                new LoadingCacheMock <string>(ref_cache_builder);

            CacheBuilder <long>     val_cache_builder = new CacheBuilder <long>();
            LoadingCacheMock <long> val_cache         =
                new LoadingCacheMock <long>(val_cache_builder);

            CacheLoader <string> ref_loader = new StringCacheLoader();

            try {
                ref_cache.Get("missing-ref-key", ref_loader);
            } catch (ExecutionException exception) {
                Assert.IsAssignableFrom <InvalidCacheLoadException>(
                    exception.InnerException);
            }

            CacheLoader <long> val_loader =
                CacheLoader <long> .From(delegate(string key) { return(default(long)); });

            Assert.DoesNotThrow(
                delegate() { val_cache.Get("missing-ref-key", val_loader); });
        }
Example #3
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));
        }
Example #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LoadingCache{T}"/> using the
 /// specified cache provider, builder and automatic loader.
 /// </summary>
 /// <param name="provider">
 /// A <see cref="ICacheProvider"/> that is used to cache object.
 /// </param>
 /// <param name="builder">
 /// A <see cref="CacheBuilder{T}"/> object that contains information about
 /// the cache configuration.
 /// </param>
 /// <param name="loader">
 /// An <see cref="CacheLoader{T}"/> that is used to automatically load new
 /// items in cache when an item for a given key does not exists or is
 /// expired.
 /// </param>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="provider"/>, <paramref name="builder"/> or
 /// <paramref name="loader"/> are <c>null</c>.
 /// </exception>
 public LoadingCache(ICacheProvider provider, CacheBuilder <T> builder,
                     CacheLoader <T> loader) : this(provider, builder)
 {
     if (loader == null)
     {
         Thrower.ThrowArgumentNullException(ExceptionArgument.loader);
     }
     default_cache_loader_ = loader;
 }
Example #5
0
 /// <summary>
 /// Schedule a refresh for an entry.
 /// </summary>
 /// <param name="entry">
 /// The entry to be refreshed.
 /// </param>
 /// <param name="key">
 /// The key associated with the entry to be refreshed.
 /// </param>
 /// <param name="old_value">
 /// The old value of the entry.
 /// </param>
 /// <param name="now">
 /// The current time, it is used to check if the entry needs a refresh.
 /// </param>
 /// <param name="loader">
 /// A <see cref="CacheLoader{T}"/> object that is used to load the new
 /// value for the entry.
 /// </param>
 /// <returns>
 /// The refreshed value if a refresh was performed; otherwise, the old
 /// value.
 /// </returns>
 T ScheduleRefresh(CacheEntry <T> entry, string key, T old_value, long now,
                   CacheLoader <T> loader)
 {
     if (Refreshes && (now - entry.WriteTime > refresh_nanos_))
     {
         T new_value;
         if (Refresh(key, loader, out new_value))
         {
             return(new_value);
         }
     }
     return(old_value);
 }
Example #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);
        }
Example #7
0
        /// <summary>
        /// Initializes a new instance of the <see cref="LoadingCache{T}"/> class
        /// by using the specified cache provider and builder.
        /// </summary>
        /// <param name="provider">
        /// A <see cref="ICacheProvider"/> that is used to cache object.
        /// </param>
        /// <param name="builder">
        /// A <see cref="CacheBuilder{T}"/> object that contains information about
        /// the cache configuration.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="provider"/> or <paramref name="builder"/> are
        /// <c>null</c>.
        /// </exception>
        public LoadingCache(ICacheProvider provider, CacheBuilder <T> builder)
        {
            if (provider == null || builder == null)
            {
                Thrower.ThrowArgumentNullException(provider == null
          ? ExceptionArgument.provider
          : ExceptionArgument.builder);
            }

            cache_provider_            = provider;
            cache_guid_                = Guid.NewGuid().ToString("N");
            expire_after_access_nanos_ = builder.ExpiryAfterAccessNanos;
            expire_after_write_nanos_  = builder.ExpiryAfterWriteNanos;
            refresh_nanos_             = builder.RefreshNanos;
            strength_type_             = builder.ValueStrength;
            mutex_                = new object();
            t_is_value_type_      = typeof(T).IsValueType;
            default_cache_loader_ =
                CacheLoader <T> .From(delegate { throw new NotSupportedException(); });
        }
Example #8
0
        /// <summary>
        /// Gets the value associated with the given key, creating or retrieving
        /// that value if necessary.
        /// </summary>
        /// <param name="key">The identifier for the cache item to retrieve.
        /// </param>
        /// <param name="loader">A <see cref="CacheLoader{T}"/> object that could
        /// be used to create the value if it is not present in the cache.</param>
        /// <remarks>
        /// No state associated with this cache is modified until loading is
        /// complete.
        /// </remarks>
        /// <exception cref="ExecutionException">A failure occur while loading
        /// the item using the specified loader delegate.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="key"/> or
        /// <paramref name="loader"/> are <c>null</c>.</exception>
        public T Get(string key, CacheLoader <T> loader)
        {
            if (key == null || loader == null)
            {
                Thrower.ThrowArgumentNullException(
                    key == null ? ExceptionArgument.key : ExceptionArgument.loader);
            }

            try {
                T value;
                CacheEntry <T> entry;

                // the cache provider should provide the thread safeness behavior for
                // the reading operation.
                bool ok = cache_provider_.Get(CacheKey(key), out entry);
                if (ok)
                {
                    long now = Clock.NanoTime;
                    if (GetLiveValue(entry, now, out value))
                    {
                        RecordRead(entry, now);
                        return(ScheduleRefresh(entry, key, value, now, loader));
                    }
                    IValueReference <T> value_reference = entry.ValueReference;
                    if (value_reference.IsLoading)
                    {
                        return(WaitForLoadingValue(entry, key, value_reference));
                    }
                }

                // at this point entry does not exists or is expired, so lets load
                // the value.
                return(LockedGetOrLoad(key, loader));
            } catch (Exception e) {
                MustLogger.ForCurrentProcess.Error(kTypeForLogger + "Get]", e);
                throw;
            }
        }
Example #9
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);
        }
Example #10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LocalManualCache{T}"/> by
 /// using the specified cache provider, builder and automatic loader.
 /// </summary>
 /// <param name="provider">
 /// A <see cref="ICacheProvider"/> object that is used to store the cached
 /// items.
 /// </param>
 /// <param name="builder">
 /// A <see cref="CacheBuilder{T}"/> containing the configured options for
 /// this cache.
 /// </param>
 /// <param name="loader">
 /// A <see cref="loader"/> that is used to automatically load values.
 /// </param>
 protected LocalManualCache(ICacheProvider provider, CacheBuilder <T> builder,
                            CacheLoader <T> loader) : base(provider, builder, loader)
 {
 }
Example #11
0
 /// <summary>
 /// Builds a cache, which either returns an already-loaded value for a
 /// given key or atomically computes or retrieves it using the supplied
 /// <see cref="CacheLoader{T}"/>.
 /// </summary>
 /// <param name="loader">
 /// The cache loader used to obtain new values.
 /// </param>
 /// <param name="provider">
 /// An object that implements the plugabble <see cref="ICacheProvider"/>
 /// interface and is used to cache the objects.
 /// </param>
 /// <returns>
 /// A <see cref="ILoadingCache{T}"/> object having the requested features.
 /// </returns>
 /// <remarks>
 /// If another thread is currently loadind the value for the given key,
 /// the returned cache, simply waits for that thread to finish and returns
 /// its loaded value. Note that multiple threads can concurrently load
 /// values for distinct keys.
 /// <para>
 /// This method does not alter the state of the
 /// <see cref="CacheBuilder{T}"/> instance, so it can be invoked again to
 /// create multiple independent caches.
 /// </para>
 /// </remarks>
 public ILoadingCache <T> Build(ICacheProvider provider, CacheLoader <T> loader)
 {
     return(new LoadingCache <T>(provider, this, loader));
 }
Example #12
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));
            }
        }