aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/Connectors/Asset
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Services/Connectors/Asset')
-rw-r--r--OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs187
1 files changed, 162 insertions, 25 deletions
diff --git a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs
index fdab254..c753c6a 100644
--- a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs
+++ b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs
@@ -30,6 +30,7 @@ using System;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.IO; 31using System.IO;
32using System.Reflection; 32using System.Reflection;
33using System.Timers;
33using Nini.Config; 34using Nini.Config;
34using OpenSim.Framework; 35using OpenSim.Framework;
35using OpenSim.Framework.Console; 36using OpenSim.Framework.Console;
@@ -47,13 +48,15 @@ namespace OpenSim.Services.Connectors
47 48
48 private string m_ServerURI = String.Empty; 49 private string m_ServerURI = String.Empty;
49 private IImprovedAssetCache m_Cache = null; 50 private IImprovedAssetCache m_Cache = null;
50 51 private int m_retryCounter;
52 private Dictionary<int, List<AssetBase>> m_retryQueue = new Dictionary<int, List<AssetBase>>();
53 private Timer m_retryTimer;
51 private delegate void AssetRetrievedEx(AssetBase asset); 54 private delegate void AssetRetrievedEx(AssetBase asset);
52 55
53 // Keeps track of concurrent requests for the same asset, so that it's only loaded once. 56 // Keeps track of concurrent requests for the same asset, so that it's only loaded once.
54 // Maps: Asset ID -> Handlers which will be called when the asset has been loaded 57 // Maps: Asset ID -> Handlers which will be called when the asset has been loaded
55 private Dictionary<string, AssetRetrievedEx> m_AssetHandlers = new Dictionary<string, AssetRetrievedEx>(); 58 private Dictionary<string, AssetRetrievedEx> m_AssetHandlers = new Dictionary<string, AssetRetrievedEx>();
56 59 private Dictionary<string, string> m_UriMap = new Dictionary<string, string>();
57 60
58 public AssetServicesConnector() 61 public AssetServicesConnector()
59 { 62 {
@@ -91,6 +94,83 @@ namespace OpenSim.Services.Connectors
91 MainConsole.Instance.Commands.AddCommand("asset", false, "dump asset", 94 MainConsole.Instance.Commands.AddCommand("asset", false, "dump asset",
92 "dump asset <id> <file>", 95 "dump asset <id> <file>",
93 "dump one cached asset", HandleDumpAsset); 96 "dump one cached asset", HandleDumpAsset);
97
98 m_retryTimer = new Timer();
99 m_retryTimer.Elapsed += new ElapsedEventHandler(retryCheck);
100 m_retryTimer.Interval = 60000;
101
102 Uri serverUri = new Uri(m_ServerURI);
103
104 string groupHost = serverUri.Host;
105
106 for (int i = 0 ; i < 256 ; i++)
107 {
108 string prefix = i.ToString("x2");
109 groupHost = assetConfig.GetString("AssetServerHost_"+prefix, groupHost);
110
111 m_UriMap[prefix] = groupHost;
112 //m_log.DebugFormat("[ASSET]: Using {0} for prefix {1}", groupHost, prefix);
113 }
114 }
115
116 private string MapServer(string id)
117 {
118 UriBuilder serverUri = new UriBuilder(m_ServerURI);
119
120 string prefix = id.Substring(0, 2).ToLower();
121
122 string host = m_UriMap[prefix];
123
124 serverUri.Host = host;
125
126 // m_log.DebugFormat("[ASSET]: Using {0} for host name for prefix {1}", host, prefix);
127
128 return serverUri.Uri.AbsoluteUri;
129 }
130
131 protected void retryCheck(object source, ElapsedEventArgs e)
132 {
133 m_retryCounter++;
134 if (m_retryCounter > 60) m_retryCounter -= 60;
135 List<int> keys = new List<int>();
136 foreach (int a in m_retryQueue.Keys)
137 {
138 keys.Add(a);
139 }
140 foreach (int a in keys)
141 {
142 //We exponentially fall back on frequency until we reach one attempt per hour
143 //The net result is that we end up in the queue for roughly 24 hours..
144 //24 hours worth of assets could be a lot, so the hope is that the region admin
145 //will have gotten the asset connector back online quickly!
146
147 int timefactor = a ^ 2;
148 if (timefactor > 60)
149 {
150 timefactor = 60;
151 }
152
153 //First, find out if we care about this timefactor
154 if (timefactor % a == 0)
155 {
156 //Yes, we do!
157 List<AssetBase> retrylist = m_retryQueue[a];
158 m_retryQueue.Remove(a);
159
160 foreach(AssetBase ass in retrylist)
161 {
162 Store(ass); //Store my ass. This function will put it back in the dictionary if it fails
163 }
164 }
165 }
166
167 if (m_retryQueue.Count == 0)
168 {
169 //It might only be one tick per minute, but I have
170 //repented and abandoned my wasteful ways
171 m_retryCounter = 0;
172 m_retryTimer.Stop();
173 }
94 } 174 }
95 175
96 protected void SetCache(IImprovedAssetCache cache) 176 protected void SetCache(IImprovedAssetCache cache)
@@ -100,13 +180,13 @@ namespace OpenSim.Services.Connectors
100 180
101 public AssetBase Get(string id) 181 public AssetBase Get(string id)
102 { 182 {
103 string uri = m_ServerURI + "/assets/" + id; 183 string uri = MapServer(id) + "/assets/" + id;
104 184
105 AssetBase asset = null; 185 AssetBase asset = null;
106 if (m_Cache != null) 186 if (m_Cache != null)
107 asset = m_Cache.Get(id); 187 asset = m_Cache.Get(id);
108 188
109 if (asset == null) 189 if (asset == null || asset.Data == null || asset.Data.Length == 0)
110 { 190 {
111 asset = SynchronousRestObjectRequester. 191 asset = SynchronousRestObjectRequester.
112 MakeRequest<int, AssetBase>("GET", uri, 0); 192 MakeRequest<int, AssetBase>("GET", uri, 0);
@@ -135,7 +215,7 @@ namespace OpenSim.Services.Connectors
135 return fullAsset.Metadata; 215 return fullAsset.Metadata;
136 } 216 }
137 217
138 string uri = m_ServerURI + "/assets/" + id + "/metadata"; 218 string uri = MapServer(id) + "/assets/" + id + "/metadata";
139 219
140 AssetMetadata asset = SynchronousRestObjectRequester. 220 AssetMetadata asset = SynchronousRestObjectRequester.
141 MakeRequest<int, AssetMetadata>("GET", uri, 0); 221 MakeRequest<int, AssetMetadata>("GET", uri, 0);
@@ -152,7 +232,7 @@ namespace OpenSim.Services.Connectors
152 return fullAsset.Data; 232 return fullAsset.Data;
153 } 233 }
154 234
155 RestClient rc = new RestClient(m_ServerURI); 235 RestClient rc = new RestClient(MapServer(id));
156 rc.AddResourcePath("assets"); 236 rc.AddResourcePath("assets");
157 rc.AddResourcePath(id); 237 rc.AddResourcePath(id);
158 rc.AddResourcePath("data"); 238 rc.AddResourcePath("data");
@@ -177,13 +257,13 @@ namespace OpenSim.Services.Connectors
177 257
178 public bool Get(string id, Object sender, AssetRetrieved handler) 258 public bool Get(string id, Object sender, AssetRetrieved handler)
179 { 259 {
180 string uri = m_ServerURI + "/assets/" + id; 260 string uri = MapServer(id) + "/assets/" + id;
181 261
182 AssetBase asset = null; 262 AssetBase asset = null;
183 if (m_Cache != null) 263 if (m_Cache != null)
184 asset = m_Cache.Get(id); 264 asset = m_Cache.Get(id);
185 265
186 if (asset == null) 266 if (asset == null || asset.Data == null || asset.Data.Length == 0)
187 { 267 {
188 lock (m_AssetHandlers) 268 lock (m_AssetHandlers)
189 { 269 {
@@ -243,38 +323,95 @@ namespace OpenSim.Services.Connectors
243 323
244 public string Store(AssetBase asset) 324 public string Store(AssetBase asset)
245 { 325 {
246 if (asset.Temporary || asset.Local) 326 // Have to assign the asset ID here. This isn't likely to
327 // trigger since current callers don't pass emtpy IDs
328 // We need the asset ID to route the request to the proper
329 // cluster member, so we can't have the server assign one.
330 if (asset.ID == string.Empty)
247 { 331 {
248 if (m_Cache != null) 332 if (asset.FullID == UUID.Zero)
249 m_Cache.Cache(asset); 333 {
334 asset.FullID = UUID.Random();
335 }
336 asset.ID = asset.FullID.ToString();
337 }
338 else if (asset.FullID == UUID.Zero)
339 {
340 UUID uuid = UUID.Zero;
341 if (UUID.TryParse(asset.ID, out uuid))
342 {
343 asset.FullID = uuid;
344 }
345 else
346 {
347 asset.FullID = UUID.Random();
348 }
349 }
250 350
351 if (m_Cache != null)
352 m_Cache.Cache(asset);
353 if (asset.Temporary || asset.Local)
354 {
251 return asset.ID; 355 return asset.ID;
252 } 356 }
253 357
254 string uri = m_ServerURI + "/assets/"; 358 string uri = MapServer(asset.FullID.ToString()) + "/assets/";
255 359
256 string newID = string.Empty; 360 string newID = string.Empty;
257 try 361 try
258 { 362 {
259 newID = SynchronousRestObjectRequester. 363 newID = SynchronousRestObjectRequester.
260 MakeRequest<AssetBase, string>("POST", uri, asset); 364 MakeRequest<AssetBase, string>("POST", uri, asset, 25);
365 if (newID == null || newID == "")
366 {
367 newID = UUID.Zero.ToString();
368 }
261 } 369 }
262 catch (Exception e) 370 catch (Exception e)
263 { 371 {
264 m_log.WarnFormat("[ASSET CONNECTOR]: Unable to send asset {0} to asset server. Reason: {1}", asset.ID, e.Message); 372 newID = UUID.Zero.ToString();
265 } 373 }
266 374
267 if (newID != String.Empty) 375 if (newID == UUID.Zero.ToString())
268 { 376 {
269 // Placing this here, so that this work with old asset servers that don't send any reply back 377 //The asset upload failed, put it in a queue for later
270 // SynchronousRestObjectRequester returns somethins that is not an empty string 378 asset.UploadAttempts++;
271 if (newID != null) 379 if (asset.UploadAttempts > 30)
272 asset.ID = newID; 380 {
381 //By this stage we've been in the queue for a good few hours;
382 //We're going to drop the asset.
383 m_log.ErrorFormat("[Assets] Dropping asset {0} - Upload has been in the queue for too long.", asset.ID.ToString());
384 }
385 else
386 {
387 if (!m_retryQueue.ContainsKey(asset.UploadAttempts))
388 {
389 m_retryQueue.Add(asset.UploadAttempts, new List<AssetBase>());
390 }
391 List<AssetBase> m_queue = m_retryQueue[asset.UploadAttempts];
392 m_queue.Add(asset);
393 m_log.WarnFormat("[Assets] Upload failed: {0} - Requeuing asset for another run.", asset.ID.ToString());
394 m_retryTimer.Start();
395 }
396 }
397 else
398 {
399 if (asset.UploadAttempts > 0)
400 {
401 m_log.InfoFormat("[Assets] Upload of {0} succeeded after {1} failed attempts", asset.ID.ToString(), asset.UploadAttempts.ToString());
402 }
403 if (newID != String.Empty)
404 {
405 // Placing this here, so that this work with old asset servers that don't send any reply back
406 // SynchronousRestObjectRequester returns somethins that is not an empty string
407 if (newID != null)
408 asset.ID = newID;
273 409
274 if (m_Cache != null) 410 if (m_Cache != null)
275 m_Cache.Cache(asset); 411 m_Cache.Cache(asset);
412 }
276 } 413 }
277 return newID; 414 return asset.ID;
278 } 415 }
279 416
280 public bool UpdateContent(string id, byte[] data) 417 public bool UpdateContent(string id, byte[] data)
@@ -295,7 +432,7 @@ namespace OpenSim.Services.Connectors
295 } 432 }
296 asset.Data = data; 433 asset.Data = data;
297 434
298 string uri = m_ServerURI + "/assets/" + id; 435 string uri = MapServer(id) + "/assets/" + id;
299 436
300 if (SynchronousRestObjectRequester. 437 if (SynchronousRestObjectRequester.
301 MakeRequest<AssetBase, bool>("POST", uri, asset)) 438 MakeRequest<AssetBase, bool>("POST", uri, asset))
@@ -310,7 +447,7 @@ namespace OpenSim.Services.Connectors
310 447
311 public bool Delete(string id) 448 public bool Delete(string id)
312 { 449 {
313 string uri = m_ServerURI + "/assets/" + id; 450 string uri = MapServer(id) + "/assets/" + id;
314 451
315 if (SynchronousRestObjectRequester. 452 if (SynchronousRestObjectRequester.
316 MakeRequest<int, bool>("DELETE", uri, 0)) 453 MakeRequest<int, bool>("DELETE", uri, 0))