Kilka dni temu, w zgodzie z tym co wykazała ankieta – Programiści to stworzenia nocne – kodowałem sobie jakieś mało znaczące rzeczy pomiędzy 2 a 3 nad ranem. Jako, że niewiele się o tej porze dzieje, można spokojnie skupić się na programowaniu :).
Jakież było moje zdziwienie, gdy o godzinie 2:28 nagle zaczęły spływać raporty błędów z dotnetomaniaka. Sam fakt pojawienia się błędów nie jest jeszcze jakiś niezwykły – wiadomo, od czasu do czasu może pójść coś nie tak. Bardziej interesujący był komunikat który pojawił się w mailu. A brzmiał on: Absolute time cannot be less than current time.
WTF – pomyślałem? Jaki czas i czemu nie może być mniejszy od obecnego? Dopiero po chwili dotarło do mnie co się dzieje. No tak – idiotyczna zmiana czasu (akurat ta jest mniej idiotyczna bo wracamy do naturalnego dla nas czasu ze sztucznego letniego).
- public override ICollection<ITag> FindByUsage(int top)
- {
- Check.Argument.IsNotNegativeOrZero(top, “top”);
- string cacheKey = “tagsByUsage:{0}”.FormatWith(top);
- ICollection<ITag> result;
- Cache.TryGet(cacheKey, out result);
- if (result == null)
- {
- result = base.FindByUsage(top);
- if ((!result.IsNullOrEmpty()) && (!Cache.Contains(cacheKey)))
- {
- Cache.Set(cacheKey, result, SystemTime.Now().AddMinutes(_cacheDurationInMinutes));
- }
- }
- return result;
- }
Powyższy kod powodował problem, a dokładniej wnętrzności metody Cache.Set. A jak ona wygląda?
- public void Set
(string key, T value, DateTime absoluteExpiration) - {
- Check.Argument.IsNotEmpty(key, “key”);
- Check.Argument.IsNotInPast(absoluteExpiration, “absoluteExpiration”);
- RemoveIfExists(key);
- _manager.Add(key, value, CacheItemPriority.Normal, null, new AbsoluteTime(absoluteExpiration.ToLocalTime()));
- }
Na początku sądziłem, że problem leży w EnterpriseLibrary Caching Block, z którego korzysta portal, ale okazało się, że w nim wszystko jest w porządku. Co prawda wyjątek leci od nich, ale dostają czas w przeszłości – co mogą zrobić? W przypadku użycia drugiej metody (z parametrem TimeSpan), kod zachowuje się poprawnie.
Zmiana czasu z 3:00 na 2:00 spowodowała, że fragment próbujący ustawić czas wygaśnięcia obiektu w cache będzie wcześniejszy niż czas dodania tegoż obiektu. I stąd komunikat. Problemem jest użycie czasu UTC i zmiana go do LocalTime przy generowaniu obiektu AbsoluteTime.
Czyj to tak na prawdę jest problem? KiGGa czy EntLib’a? Wydaje mi się, że bardziej KiGG’a bo używa metody z parametrem AbsoluteTime, który ustawia nieprawidłowo. Robi to jednak nieświadomie, bo błędne działanie jest tylko raz w roku.
Pytanie tylko jak pamiętać o takich przypadkach i co ciekawsze jak je testować i zabezpieczać się aby nie występowały? Czy lepiej olać to i nie przejmować się tak rzadko występującymi problemami?
Founder of Octal Solutions a .NET software house.
Passionate dev, blogger, occasionally speaker, one of the leaders of Wroc.NET user group. Microsoft MVP. Podcaster – Ostrapila.pl