Example #1
0
        protected void Application_Start()
        {
            // disable the X-AspNetMvc-Version: header
            MvcHandler.DisableMvcResponseHeader = true;

            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
            RegisterBundles(BundleTable.Bundles);
            //BundleTable.EnableOptimizations = true;

            SetupMiniProfiler();

            Exceptional.Exceptional.Settings.GetCustomData = GetCustomErrorData;

            TaskScheduler.UnobservedTaskException += (sender, args) => Current.LogException(args.Exception);

            // enable custom model binder
            ModelBinders.Binders.DefaultBinder = new ProfiledModelBinder();

            // When settings change, reload the app pool
            Current.Settings.OnChanged += HttpRuntime.UnloadAppDomain;

            PollingEngine.Configure(t => HostingEnvironment.QueueBackgroundWorkItem(_ => t()));
        }
Example #2
0
        /// <summary>
        ///
        /// lookup refreshes the data if necessary, passing the old data if we have it.
        ///
        /// durationSecs is the "time before stale" for the data
        /// serveStaleSecs is the maximum amount of time to serve data once it becomes stale
        ///
        /// Note that one unlucky caller when the data is stale will block to fill the cache,
        /// everybody else will get stale data though.
        /// </summary>
        public static T GetSet <T>(this LocalCache cache, string key, Func <T, MicroContext, T> lookup, int durationSecs, int serveStaleDataSecs)
            where T : class
        {
            var possiblyStale = cache.Get <GetSetWrapper <T> >(key);
            var localLockName = key;
            var nullLoadLock  = _getSetNullLocks.AddOrUpdate(localLockName, k => new object(), (k, old) => old);

            if (possiblyStale == null)
            {
                // We can't prevent multiple web server's from running this (well, we can but its probably overkill) but we can
                //   at least stop the query from running multiple times on *this* web server
                lock (nullLoadLock)
                {
                    possiblyStale = cache.Get <GetSetWrapper <T> >(key);

                    if (possiblyStale == null)
                    {
                        T data;
                        using (var ctx = new MicroContext())
                        {
                            data = lookup(null, ctx);
                        }
                        possiblyStale = new GetSetWrapper <T>
                        {
                            Data       = data,
                            StaleAfter = DateTime.UtcNow + TimeSpan.FromSeconds(durationSecs)
                        };

                        cache.Set(key, possiblyStale, durationSecs + serveStaleDataSecs);
                        Interlocked.Increment(ref totalGetSetSync);
                    }
                }
            }

            if (possiblyStale.StaleAfter > DateTime.UtcNow)
            {
                return(possiblyStale.Data);
            }

            bool gotCompeteLock = false;

            if (Monitor.TryEnter(nullLoadLock, 0))
            {   // it isn't actively being refreshed; we'll check for a mutex on the cache
                try
                {
                    gotCompeteLock = GotCompeteLock(cache, key);
                }
                finally
                {
                    Monitor.Exit(nullLoadLock);
                }
            }

            if (gotCompeteLock)
            {
                var old  = possiblyStale.Data;
                var task = new Task(delegate
                {
                    lock (nullLoadLock) // holding this lock allows us to locally short-circuit all the other threads that come asking
                    {
                        try
                        {
                            var updated = new GetSetWrapper <T>();
                            using (var ctx = new MicroContext())
                            {
                                updated.Data       = lookup(old, ctx);
                                updated.StaleAfter = DateTime.UtcNow + TimeSpan.FromSeconds(durationSecs);
                            }
                            cache.Remove(key);
                            cache.Set(key, updated, durationSecs + serveStaleDataSecs);
                        }
                        finally
                        {
                            ReleaseCompeteLock(cache, key);
                        }
                    }
                });
                task.ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        Interlocked.Increment(ref totalGetSetAsyncError);
                        Current.LogException(t.Exception);
                    }
                    else
                    {
                        Interlocked.Increment(ref totalGetSetAsyncSuccess);
                    }
                });
                task.Start();
            }

            return(possiblyStale.Data);
        }
Example #3
0
        /// <summary>
        /// Updates this settings object, return true if there was an actual change
        /// </summary>
        public virtual bool UpdateSettings(T newSettings)
        {
            //Current.LogException("Settings updated! " + GetType(), null);
            if (newSettings == null)
            {
                //Current.LogException("Updated settings for " + typeof(T).Name + " were null, aborting.", null);
                return(false);
            }

            bool changed = false;

            try
            {
                var toCopy = Properties;
                foreach (var prop in toCopy)
                {
                    if (!prop.CanWrite)
                    {
                        continue;
                    }

                    var current    = prop.GetValue(this);
                    var newSetting = prop.GetValue(newSettings);

                    // observables are meant to be updated at the member level and need specific love
                    if (prop.PropertyType.IsGenericType &&
                        prop.PropertyType.GetGenericTypeDefinition() == typeof(ObservableCollection <>))
                    {
                        // handle collections!
                        // TODO: Decide on how collections (and subcollections) are handled
                        // need to broadcast these changes or publish them via json APIs
                    }
                    else if (prop.CanWrite)
                    {
                        try
                        {
                            if (current == newSetting)
                            {
                                continue;                        // nothing changed, NEXT
                            }
                            prop.SetValue(this, newSetting);
                            OnPropertyChanged(prop.Name);
                            changed = true;
                        }
                        catch (Exception e)
                        {
                            Current.LogException("Error setting propery: " + prop.Name, e);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Current.LogException("Error updating settings for " + typeof(T).Name, e);
            }

            if (changed)
            {
                TriggerChanged();
            }
            return(changed);
        }