1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef __DEFAULT_BROWSER_AGENT_CACHE_H__
8 #define __DEFAULT_BROWSER_AGENT_CACHE_H__
16 namespace mozilla::default_agent
{
18 using DwordResult
= mozilla::WindowsErrorResult
<uint32_t>;
21 * This cache functions as a FIFO queue which writes its data to the Windows
24 * Note that the cache is not thread-safe, so it is recommended that the WDBA's
25 * RegistryMutex be acquired before accessing it.
27 * Some of the terminology used in this module is a easy to mix up, so let's
28 * just be clear about it:
29 * - registry key/sub-key
30 * A registry key is sort of like the registry's equivalent of a
31 * directory. It can contain values, each of which is made up of a name
32 * and corresponding data. We may also refer to a "sub-key", meaning a
33 * registry key nested in a registry key.
34 * - cache key/entry key
35 * A cache key refers to the string that we use to look up a single
36 * element of cache entry data. Example: "CacheEntryVersion"
38 * This refers to an entire record stored using Cache::Enqueue or retrieved
39 * using Cache::Dequeue. It consists of numerous cache keys and their
42 * The first version of this cache was problematic because of how hard it was to
43 * extend. This version attempts to overcome this. It first migrates all data
44 * out of the version 1 cache. This means that the stored ping data will not
45 * be accessible to out-of-date clients, but presumably they will eventually
46 * be updated or the up-to-date client that performed the migration will send
47 * the pings itself. Because the WDBA telemetry has no client ID, all analysis
48 * is stateless, so even if the other clients send some pings before the stored
49 * ones get sent, that's ok. The ordering isn't really important.
51 * This version of the cache attempts to correct the problem of how hard it was
52 * to extend the old cache. The biggest problem that the old cache had was that
53 * when it dequeued data it had to shift data, but it wouldn't shift keys that
54 * it didn't know about, causing them to become associated with the wrong cache
57 * Version 2 of the cache will make 4 improvements to attempt to avoid problems
58 * like this in the future:
59 * 1. Each cache entry will get its own registry key. This will help to keep
60 * cache entries isolated from each other.
61 * 2. Each cache entry will include version data so that we know what cache
62 * keys to expect when we read it.
63 * 3. Rather than having to shift every entry every time we dequeue, we will
64 * implement a circular queue so that we just have to update what index
65 * currently represents the front
66 * 4. We will store the cache capacity in the cache so that we can expand the
67 * cache later, if we want, without breaking previous versions.
71 // cacheRegKey is the registry sub-key that the cache will be stored in. If
72 // null is passed (the default), we will use the default cache name. This is
73 // what ought to be used in production. When testing, we will pass a different
74 // key in so that our testing caches don't conflict with each other or with
75 // a possible production cache on the test machine.
76 explicit Cache(const wchar_t* cacheRegKey
= nullptr);
79 // The version of the cache (not to be confused with the version of the cache
80 // entries). This should only be incremented if we need to make breaking
81 // changes that require migration to a new cache location, like we did between
82 // versions 1 and 2. This value will be used as part of the sub-key that the
83 // cache is stored in (ex: "PingCache\version2").
84 static constexpr const uint32_t kVersion
= 2;
85 // This value will be written into each entry. This allows us to know what
86 // cache keys to expect in the event that additional cache keys are added in
87 // later entry versions.
88 static constexpr const uint32_t kEntryVersion
= 2;
89 static constexpr const uint32_t kDefaultCapacity
= 2;
90 // We want to allow the cache to be expandable, but we don't really want it to
91 // be infinitely expandable. So we'll set an upper bound.
92 static constexpr const uint32_t kMaxCapacity
= 100;
93 static constexpr const wchar_t* kDefaultPingCacheRegKey
= L
"PingCache";
95 // Used to read the version 1 cache entries during data migration. Full cache
96 // key names are formatted like: "<keyPrefix><baseKeyName><cacheIndex>"
97 // For example: "PingCacheNotificationType0"
98 static constexpr const wchar_t* kVersion1KeyPrefix
= L
"PingCache";
99 static constexpr const uint32_t kVersion1MaxSize
= 2;
101 static constexpr const wchar_t* kCapacityRegName
= L
"Capacity";
102 static constexpr const wchar_t* kFrontRegName
= L
"Front";
103 static constexpr const wchar_t* kSizeRegName
= L
"Size";
106 static constexpr const wchar_t* kEntryVersionKey
= L
"CacheEntryVersion";
107 // Note that the next 3 must also match the base key names from version 1
108 // since we use them to construct those key names.
109 static constexpr const wchar_t* kNotificationTypeKey
= L
"NotificationType";
110 static constexpr const wchar_t* kNotificationShownKey
= L
"NotificationShown";
111 static constexpr const wchar_t* kNotificationActionKey
=
112 L
"NotificationAction";
113 static constexpr const wchar_t* kPrevNotificationActionKey
=
114 L
"PrevNotificationAction";
116 // The version key wasn't added until version 2, but we add it to the version
117 // 1 entries when migrating them to the cache.
118 static constexpr const uint32_t kInitialVersionEntryVersionKey
= 1;
119 static constexpr const uint32_t kInitialVersionNotificationTypeKey
= 1;
120 static constexpr const uint32_t kInitialVersionNotificationShownKey
= 1;
121 static constexpr const uint32_t kInitialVersionNotificationActionKey
= 1;
122 static constexpr const uint32_t kInitialVersionPrevNotificationActionKey
= 2;
124 // We have two cache entry structs: one for the current version, and one
125 // generic one that can handle any version. There are a couple of reasons
127 // - We only want to support writing the current version, but we want to
128 // support reading any version.
129 // - It makes things a bit nicer for the caller when Enqueue-ing, since
130 // they don't have to set the version or wrap values that were added
131 // later in a mozilla::Maybe.
132 // - It keeps us from having to worry about writing an invalid cache entry,
133 // such as one that claims to be version 2, but doesn't have
134 // prevNotificationAction.
135 // Note that the entry struct for the current version does not contain a
136 // version member value because we already know that its version is equal to
137 // Cache::kEntryVersion.
139 std::string notificationType
;
140 std::string notificationShown
;
141 std::string notificationAction
;
142 std::string prevNotificationAction
;
144 struct VersionedEntry
{
145 uint32_t entryVersion
;
146 std::string notificationType
;
147 std::string notificationShown
;
148 std::string notificationAction
;
149 mozilla::Maybe
<std::string
> prevNotificationAction
;
152 using MaybeEntry
= mozilla::Maybe
<VersionedEntry
>;
153 using MaybeEntryResult
= mozilla::WindowsErrorResult
<MaybeEntry
>;
156 VoidResult
Enqueue(const Entry
& entry
);
157 MaybeEntryResult
Dequeue();
160 const std::wstring mCacheRegKey
;
162 // We can't easily copy a VoidResult, so just store the raw HRESULT here.
163 mozilla::Maybe
<HRESULT
> mInitializeResult
;
164 // How large the cache will grow before it starts rejecting new entries.
166 // The index of the first present cache entry.
168 // How many entries are present in the cache.
171 DwordResult
EnsureDwordSetting(const wchar_t* regName
, uint32_t defaultValue
);
172 VoidResult
SetupCache();
173 VoidResult
MaybeMigrateVersion1();
174 std::wstring
MakeEntryRegKeyName(uint32_t index
);
175 VoidResult
WriteEntryKeys(uint32_t index
, const VersionedEntry
& entry
);
176 VoidResult
DeleteEntry(uint32_t index
);
177 VoidResult
SetFront(uint32_t newFront
);
178 VoidResult
SetSize(uint32_t newSize
);
179 VoidResult
VersionedEnqueue(const VersionedEntry
& entry
);
180 VoidResult
DiscardFront();
181 MaybeDwordResult
ReadEntryKeyDword(const std::wstring
& regKey
,
182 const wchar_t* regName
, bool expected
);
183 MaybeStringResult
ReadEntryKeyString(const std::wstring
& regKey
,
184 const wchar_t* regName
, bool expected
);
187 } // namespace mozilla::default_agent
189 #endif // __DEFAULT_BROWSER_AGENT_CACHE_H__