diff options
author | Dahlia Trimble | 2009-06-02 22:42:47 +0000 |
---|---|---|
committer | Dahlia Trimble | 2009-06-02 22:42:47 +0000 |
commit | b38be1a7fdf71a7d3c6d6f761590827b1f45483c (patch) | |
tree | 2be409eb714dfd93d78e50d8b0def07b8e9758e1 /OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs | |
parent | Thank you kindly, MattSetzer, for a patch that solves a Mantis: (diff) | |
download | opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.zip opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.gz opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.bz2 opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.xz |
Thank you Imaze Rhiano for a patch that implements Cenome Memory Asset Cache (Mantis #3759)
See the files: bin/config-include/GridCommon.ini.example and bin/config-include/StandaloneCommon.ini.example to configure and enable this caching method.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs new file mode 100644 index 0000000..4a34ca0 --- /dev/null +++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using log4net; | ||
31 | using Nini.Config; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Framework.Interfaces; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.CoreModules.Asset | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// Cenome memory asset cache. | ||
40 | /// </summary> | ||
41 | /// <remarks> | ||
42 | /// <para> | ||
43 | /// Cache is enabled by setting "AssetCaching" configuration to value "CenomeMemoryAssetCache". | ||
44 | /// When cache is successfully enable log should have message | ||
45 | /// "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = XXX bytes, MaxCount = XXX, ExpirationTime = XXX)". | ||
46 | /// </para> | ||
47 | /// <para> | ||
48 | /// Cache's size is limited by two parameters: | ||
49 | /// maximal allowed size in bytes and maximal allowed asset count. When new asset | ||
50 | /// is added to cache that have achieved either size or count limitation, cache | ||
51 | /// will automatically remove less recently used assets from cache. Additionally | ||
52 | /// asset's lifetime is controlled by expiration time. | ||
53 | /// </para> | ||
54 | /// <para> | ||
55 | /// <list type="table"> | ||
56 | /// <listheader> | ||
57 | /// <term>Configuration</term> | ||
58 | /// <description>Description</description> | ||
59 | /// </listheader> | ||
60 | /// <item> | ||
61 | /// <term>MaxSize</term> | ||
62 | /// <description>Maximal size of the cache in bytes. Default value: 128MB (134 217 728 bytes).</description> | ||
63 | /// </item> | ||
64 | /// <item> | ||
65 | /// <term>MaxCount</term> | ||
66 | /// <description>Maximal count of assets stored to cache. Default value: 4096 assets.</description> | ||
67 | /// </item> | ||
68 | /// <item> | ||
69 | /// <term>ExpirationTime</term> | ||
70 | /// <description>Asset's expiration time in minutes. Default value: 30 minutes.</description> | ||
71 | /// </item> | ||
72 | /// </list> | ||
73 | /// </para> | ||
74 | /// </remarks> | ||
75 | /// <example> | ||
76 | /// Enabling Cenome Asset Cache: | ||
77 | /// <code> | ||
78 | /// [Modules] | ||
79 | /// AssetCaching = "CenomeMemoryAssetCache" | ||
80 | /// </code> | ||
81 | /// Setting size and expiration time limitations: | ||
82 | /// <code> | ||
83 | /// [AssetService] | ||
84 | /// ; 256 MB (default: 134217728) | ||
85 | /// MaxSize = 268435456 | ||
86 | /// ; How many assets it is possible to store cache (default: 4096) | ||
87 | /// MaxCount = 16384 | ||
88 | /// ; Expiration time - 1 hour (default: 30 minutes) | ||
89 | /// ExpirationTime = 60 | ||
90 | /// </code> | ||
91 | /// </example> | ||
92 | public class CenomeMemoryAssetCache : IImprovedAssetCache, ISharedRegionModule | ||
93 | { | ||
94 | /// <summary> | ||
95 | /// Cache's default maximal asset count. | ||
96 | /// </summary> | ||
97 | /// <remarks> | ||
98 | /// <para> | ||
99 | /// Assuming that average asset size is about 32768 bytes. | ||
100 | /// </para> | ||
101 | /// </remarks> | ||
102 | public const int DefaultMaxCount = 4096; | ||
103 | |||
104 | /// <summary> | ||
105 | /// Default maximal size of the cache in bytes | ||
106 | /// </summary> | ||
107 | /// <remarks> | ||
108 | /// <para> | ||
109 | /// 128MB = 128 * 1024^2 = 134 217 728 bytes. | ||
110 | /// </para> | ||
111 | /// </remarks> | ||
112 | public const long DefaultMaxSize = 134217728; | ||
113 | |||
114 | /// <summary> | ||
115 | /// Asset's default expiration time in the cache. | ||
116 | /// </summary> | ||
117 | public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 ); | ||
118 | |||
119 | /// <summary> | ||
120 | /// Log manager instance. | ||
121 | /// </summary> | ||
122 | private static readonly ILog Log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType ); | ||
123 | |||
124 | /// <summary> | ||
125 | /// Cache object. | ||
126 | /// </summary> | ||
127 | private ICnmCache<string, AssetBase> m_cache; | ||
128 | |||
129 | /// <summary> | ||
130 | /// Is Cenome asset cache enabled. | ||
131 | /// </summary> | ||
132 | private bool m_enabled; | ||
133 | |||
134 | /// <summary> | ||
135 | /// Count of get requests | ||
136 | /// </summary> | ||
137 | private int m_getCount; | ||
138 | |||
139 | /// <summary> | ||
140 | /// How many hits | ||
141 | /// </summary> | ||
142 | private int m_hitCount; | ||
143 | |||
144 | /// <summary> | ||
145 | /// Count of cache commands | ||
146 | /// </summary> | ||
147 | private int m_cachedCount; | ||
148 | |||
149 | /// <summary> | ||
150 | /// How many gets before dumping statistics | ||
151 | /// </summary> | ||
152 | /// <remarks> | ||
153 | /// If 0 or less, then disabled. | ||
154 | /// </remarks> | ||
155 | private int m_debugEpoch = 0; | ||
156 | |||
157 | /// <summary> | ||
158 | /// Initialize asset cache module with default parameters. | ||
159 | /// </summary> | ||
160 | public void Initialize() | ||
161 | { | ||
162 | Initialize( DefaultMaxSize, DefaultMaxCount, DefaultExpirationTime ); | ||
163 | } | ||
164 | |||
165 | /// <summary> | ||
166 | /// Initialize asset cache module, with custom parameters. | ||
167 | /// </summary> | ||
168 | /// <param name="maximalSize"> | ||
169 | /// Cache's maximal size in bytes. | ||
170 | /// </param> | ||
171 | /// <param name="maximalCount"> | ||
172 | /// Cache's maximal count of assets. | ||
173 | /// </param> | ||
174 | /// <param name="expirationTime"> | ||
175 | /// Asset's expiration time. | ||
176 | /// </param> | ||
177 | public void Initialize( long maximalSize, int maximalCount, TimeSpan expirationTime ) | ||
178 | { | ||
179 | if( maximalSize <= 0 || maximalCount <= 0 || expirationTime <= TimeSpan.Zero ) | ||
180 | { | ||
181 | Log.Info( "[ASSET CACHE]: Cenome asset cache is not enabled." ); | ||
182 | m_enabled = false; | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | // Create cache and add synchronization wrapper over it | ||
187 | m_cache = | ||
188 | CnmSynchronizedCache<string, AssetBase>.Synchronized( new CnmMemoryCache<string, AssetBase>( | ||
189 | maximalSize, maximalCount, expirationTime ) ); | ||
190 | m_enabled = true; | ||
191 | Log.InfoFormat("[ASSET CACHE]: Cenome asset cache enabled (MaxSize = {0} bytes, MaxCount = {1}, ExpirationTime = {2})", maximalSize, maximalCount, expirationTime ); | ||
192 | } | ||
193 | |||
194 | #region IImprovedAssetCache Members | ||
195 | /// <summary> | ||
196 | /// Cache asset. | ||
197 | /// </summary> | ||
198 | /// <param name="asset"> | ||
199 | /// The asset that is being cached. | ||
200 | /// </param> | ||
201 | public void Cache( AssetBase asset ) | ||
202 | { | ||
203 | long size = asset.Data != null ? asset.Data.Length : 1; | ||
204 | m_cache.Set( asset.ID, asset, size ); | ||
205 | m_cachedCount++; | ||
206 | } | ||
207 | |||
208 | /// <summary> | ||
209 | /// Clear asset cache. | ||
210 | /// </summary> | ||
211 | public void Clear() | ||
212 | { | ||
213 | m_cache.Clear(); | ||
214 | } | ||
215 | |||
216 | /// <summary> | ||
217 | /// Expire (remove) asset stored to cache. | ||
218 | /// </summary> | ||
219 | /// <param name="id"> | ||
220 | /// The expired asset's id. | ||
221 | /// </param> | ||
222 | public void Expire( string id ) | ||
223 | { | ||
224 | m_cache.Remove( id ); | ||
225 | } | ||
226 | |||
227 | /// <summary> | ||
228 | /// Get asset stored | ||
229 | /// </summary> | ||
230 | /// <param name="id"> | ||
231 | /// The asset's id. | ||
232 | /// </param> | ||
233 | /// <returns> | ||
234 | /// Asset if it is found from cache; otherwise <see langword="null"/>. | ||
235 | /// </returns> | ||
236 | /// <remarks> | ||
237 | /// <para> | ||
238 | /// Caller should always check that is return value <see langword="null"/>. | ||
239 | /// Cache doesn't guarantee in any situation that asset is stored to it. | ||
240 | /// </para> | ||
241 | /// </remarks> | ||
242 | public AssetBase Get( string id ) | ||
243 | { | ||
244 | m_getCount++; | ||
245 | AssetBase assetBase; | ||
246 | if( m_cache.TryGetValue( id, out assetBase ) ) | ||
247 | m_hitCount++; | ||
248 | |||
249 | if( m_getCount == m_debugEpoch ) | ||
250 | { | ||
251 | Log.InfoFormat( "[ASSET CACHE]: Cached = {0}, Get = {1}, Hits = {2}%, Size = {3} bytes, Avg. A. Size = {4} bytes", | ||
252 | m_cachedCount, m_getCount, ( (double) m_hitCount / m_getCount ) * 100.0, m_cache.Size, m_cache.Size / m_cache.Count ); | ||
253 | m_getCount = 0; | ||
254 | m_hitCount = 0; | ||
255 | m_cachedCount = 0; | ||
256 | } | ||
257 | |||
258 | return assetBase; | ||
259 | } | ||
260 | #endregion | ||
261 | |||
262 | #region ISharedRegionModule Members | ||
263 | /// <summary> | ||
264 | /// Gets region module's name. | ||
265 | /// </summary> | ||
266 | public string Name | ||
267 | { | ||
268 | get { return "CenomeMemoryAssetCache"; } | ||
269 | } | ||
270 | |||
271 | /// <summary> | ||
272 | /// New region is being added to server. | ||
273 | /// </summary> | ||
274 | /// <param name="scene"> | ||
275 | /// Region's scene. | ||
276 | /// </param> | ||
277 | public void AddRegion( Scene scene ) | ||
278 | { | ||
279 | if( m_enabled ) | ||
280 | scene.RegisterModuleInterface<IImprovedAssetCache>( this ); | ||
281 | } | ||
282 | |||
283 | /// <summary> | ||
284 | /// Close region module. | ||
285 | /// </summary> | ||
286 | public void Close() | ||
287 | { | ||
288 | m_enabled = false; | ||
289 | m_cache.Clear(); | ||
290 | m_cache = null; | ||
291 | } | ||
292 | |||
293 | /// <summary> | ||
294 | /// Initialize region module. | ||
295 | /// </summary> | ||
296 | /// <param name="source"> | ||
297 | /// Configuration source. | ||
298 | /// </param> | ||
299 | public void Initialise( IConfigSource source ) | ||
300 | { | ||
301 | m_cache = null; | ||
302 | m_enabled = false; | ||
303 | |||
304 | var moduleConfig = source.Configs[ "Modules" ]; | ||
305 | if( moduleConfig == null ) | ||
306 | return; | ||
307 | |||
308 | var name = moduleConfig.GetString( "AssetCaching" ); | ||
309 | Log.DebugFormat( "[XXX] name = {0} (this module's name: {1}", name, Name ); | ||
310 | |||
311 | if( name != Name ) | ||
312 | return; | ||
313 | |||
314 | // This module is used | ||
315 | var maxSize = DefaultMaxSize; | ||
316 | var maxCount = DefaultMaxCount; | ||
317 | var expirationTime = DefaultExpirationTime; | ||
318 | |||
319 | var assetConfig = source.Configs[ "AssetCache" ]; | ||
320 | if( assetConfig != null ) | ||
321 | { | ||
322 | // Get optional configurations | ||
323 | maxSize = assetConfig.GetLong( "MaxSize", DefaultMaxSize ); | ||
324 | maxCount = assetConfig.GetInt( "MaxCount", DefaultMaxCount ); | ||
325 | expirationTime = | ||
326 | TimeSpan.FromMinutes( assetConfig.GetInt( "ExpirationTime", (int) DefaultExpirationTime.TotalMinutes ) ); | ||
327 | |||
328 | // Debugging purposes only | ||
329 | m_debugEpoch = assetConfig.GetInt( "DebugEpoch", 0 ); | ||
330 | } | ||
331 | |||
332 | Initialize( maxSize, maxCount, expirationTime ); | ||
333 | } | ||
334 | |||
335 | /// <summary> | ||
336 | /// Initialization post handling. | ||
337 | /// </summary> | ||
338 | /// <remarks> | ||
339 | /// <para> | ||
340 | /// Modules can use this to initialize connection with other modules. | ||
341 | /// </para> | ||
342 | /// </remarks> | ||
343 | public void PostInitialise() | ||
344 | { | ||
345 | } | ||
346 | |||
347 | /// <summary> | ||
348 | /// Region has been loaded. | ||
349 | /// </summary> | ||
350 | /// <param name="scene"> | ||
351 | /// Region's scene. | ||
352 | /// </param> | ||
353 | /// <remarks> | ||
354 | /// <para> | ||
355 | /// This is needed for all module types. Modules will register | ||
356 | /// Interfaces with scene in AddScene, and will also need a means | ||
357 | /// to access interfaces registered by other modules. Without | ||
358 | /// this extra method, a module attempting to use another modules' | ||
359 | /// interface would be successful only depending on load order, | ||
360 | /// which can't be depended upon, or modules would need to resort | ||
361 | /// to ugly kludges to attempt to request interfaces when needed | ||
362 | /// and unnecessary caching logic repeated in all modules. | ||
363 | /// The extra function stub is just that much cleaner. | ||
364 | /// </para> | ||
365 | /// </remarks> | ||
366 | public void RegionLoaded( Scene scene ) | ||
367 | { | ||
368 | } | ||
369 | |||
370 | /// <summary> | ||
371 | /// Region is being removed. | ||
372 | /// </summary> | ||
373 | /// <param name="scene"> | ||
374 | /// Region scene that is being removed. | ||
375 | /// </param> | ||
376 | public void RemoveRegion( Scene scene ) | ||
377 | { | ||
378 | } | ||
379 | #endregion | ||
380 | } | ||
381 | } | ||