diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs | 232 |
1 files changed, 56 insertions, 176 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs index db66c83..91f4dc3 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs | |||
@@ -60,29 +60,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
60 | Completed, | 60 | Completed, |
61 | Aborted | 61 | Aborted |
62 | }; | 62 | }; |
63 | |||
64 | /// <value> | ||
65 | /// Timeout threshold if we still need assets or missing asset notifications but have stopped receiving them | ||
66 | /// from the asset service | ||
67 | /// </value> | ||
68 | protected const int TIMEOUT = 60 * 1000; | ||
69 | |||
70 | /// <value> | ||
71 | /// If a timeout does occur, limit the amount of UUID information put to the console. | ||
72 | /// </value> | ||
73 | protected const int MAX_UUID_DISPLAY_ON_TIMEOUT = 3; | ||
74 | |||
75 | protected System.Timers.Timer m_requestCallbackTimer; | ||
76 | 63 | ||
77 | /// <value> | 64 | /// <value> |
78 | /// State of this request | ||
79 | /// </value> | ||
80 | private RequestState m_requestState = RequestState.Initial; | ||
81 | |||
82 | /// <value> | ||
83 | /// uuids to request | 65 | /// uuids to request |
84 | /// </value> | 66 | /// </value> |
85 | protected IDictionary<UUID, sbyte> m_uuids; | 67 | protected IDictionary<UUID, sbyte> m_uuids; |
68 | private int m_previousErrorsCount; | ||
86 | 69 | ||
87 | /// <value> | 70 | /// <value> |
88 | /// Callback used when all the assets requested have been received. | 71 | /// Callback used when all the assets requested have been received. |
@@ -93,7 +76,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
93 | /// List of assets that were found. This will be passed back to the requester. | 76 | /// List of assets that were found. This will be passed back to the requester. |
94 | /// </value> | 77 | /// </value> |
95 | protected List<UUID> m_foundAssetUuids = new List<UUID>(); | 78 | protected List<UUID> m_foundAssetUuids = new List<UUID>(); |
96 | 79 | ||
97 | /// <value> | 80 | /// <value> |
98 | /// Maintain a list of assets that could not be found. This will be passed back to the requester. | 81 | /// Maintain a list of assets that could not be found. This will be passed back to the requester. |
99 | /// </value> | 82 | /// </value> |
@@ -104,217 +87,114 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
104 | /// </value> | 87 | /// </value> |
105 | private int m_repliesRequired; | 88 | private int m_repliesRequired; |
106 | 89 | ||
90 | private System.Timers.Timer m_timeOutTimer; | ||
91 | private bool m_timeout; | ||
92 | |||
107 | /// <value> | 93 | /// <value> |
108 | /// Asset service used to request the assets | 94 | /// Asset service used to request the assets |
109 | /// </value> | 95 | /// </value> |
110 | protected IAssetService m_assetService; | 96 | protected IAssetService m_assetService; |
111 | protected IUserAccountService m_userAccountService; | 97 | protected IUserAccountService m_userAccountService; |
112 | protected UUID m_scopeID; // the grid ID | 98 | protected UUID m_scopeID; // the grid ID |
113 | 99 | ||
114 | protected AssetsArchiver m_assetsArchiver; | 100 | protected AssetsArchiver m_assetsArchiver; |
115 | 101 | ||
116 | protected Dictionary<string, object> m_options; | 102 | protected Dictionary<string, object> m_options; |
117 | 103 | ||
118 | protected internal AssetsRequest( | 104 | protected internal AssetsRequest( |
119 | AssetsArchiver assetsArchiver, IDictionary<UUID, sbyte> uuids, | 105 | AssetsArchiver assetsArchiver, IDictionary<UUID, sbyte> uuids, |
120 | IAssetService assetService, IUserAccountService userService, | 106 | int previousErrorsCount, |
121 | UUID scope, Dictionary<string, object> options, | 107 | IAssetService assetService, IUserAccountService userService, |
108 | UUID scope, Dictionary<string, object> options, | ||
122 | AssetsRequestCallback assetsRequestCallback) | 109 | AssetsRequestCallback assetsRequestCallback) |
123 | { | 110 | { |
124 | m_assetsArchiver = assetsArchiver; | 111 | m_assetsArchiver = assetsArchiver; |
125 | m_uuids = uuids; | 112 | m_uuids = uuids; |
113 | m_previousErrorsCount = previousErrorsCount; | ||
126 | m_assetsRequestCallback = assetsRequestCallback; | 114 | m_assetsRequestCallback = assetsRequestCallback; |
127 | m_assetService = assetService; | 115 | m_assetService = assetService; |
128 | m_userAccountService = userService; | 116 | m_userAccountService = userService; |
129 | m_scopeID = scope; | 117 | m_scopeID = scope; |
130 | m_options = options; | 118 | m_options = options; |
131 | m_repliesRequired = uuids.Count; | 119 | m_repliesRequired = uuids.Count; |
132 | |||
133 | // FIXME: This is a really poor way of handling the timeout since it will always leave the original requesting thread | ||
134 | // hanging. Need to restructure so an original request thread waits for a ManualResetEvent on asset received | ||
135 | // so we can properly abort that thread. Or request all assets synchronously, though that would be a more | ||
136 | // radical change | ||
137 | m_requestCallbackTimer = new System.Timers.Timer(TIMEOUT); | ||
138 | m_requestCallbackTimer.AutoReset = false; | ||
139 | m_requestCallbackTimer.Elapsed += new ElapsedEventHandler(OnRequestCallbackTimeout); | ||
140 | } | 120 | } |
141 | 121 | ||
142 | protected internal void Execute() | 122 | protected internal void Execute() |
143 | { | 123 | { |
144 | m_requestState = RequestState.Running; | 124 | Culture.SetCurrentCulture(); |
145 | |||
146 | m_log.DebugFormat("[ARCHIVER]: AssetsRequest executed looking for {0} possible assets", m_repliesRequired); | ||
147 | |||
148 | // We can stop here if there are no assets to fetch | 125 | // We can stop here if there are no assets to fetch |
149 | if (m_repliesRequired == 0) | 126 | if (m_repliesRequired == 0) |
150 | { | 127 | { |
151 | m_requestState = RequestState.Completed; | ||
152 | PerformAssetsRequestCallback(false); | 128 | PerformAssetsRequestCallback(false); |
153 | return; | 129 | return; |
154 | } | 130 | } |
155 | 131 | ||
156 | m_requestCallbackTimer.Enabled = true; | 132 | m_timeOutTimer = new System.Timers.Timer(60000); |
133 | m_timeOutTimer .AutoReset = false; | ||
134 | m_timeOutTimer.Elapsed += OnTimeout; | ||
135 | m_timeout = false; | ||
157 | 136 | ||
158 | foreach (KeyValuePair<UUID, sbyte> kvp in m_uuids) | 137 | foreach (KeyValuePair<UUID, sbyte> kvp in m_uuids) |
159 | { | 138 | { |
160 | // m_log.DebugFormat("[ARCHIVER]: Requesting asset {0}", kvp.Key); | 139 | string thiskey = kvp.Key.ToString(); |
161 | 140 | try | |
162 | // m_assetService.Get(kvp.Key.ToString(), kvp.Value, PreAssetRequestCallback); | 141 | { |
163 | AssetBase asset = m_assetService.Get(kvp.Key.ToString()); | 142 | m_timeOutTimer.Enabled = true; |
164 | PreAssetRequestCallback(kvp.Key.ToString(), kvp.Value, asset); | 143 | AssetBase asset = m_assetService.Get(thiskey); |
165 | } | 144 | if(m_timeout) |
166 | } | 145 | break; |
146 | |||
147 | m_timeOutTimer.Enabled = false; | ||
167 | 148 | ||
168 | protected void OnRequestCallbackTimeout(object source, ElapsedEventArgs args) | 149 | if(asset == null) |
169 | { | 150 | { |
170 | bool timedOut = true; | 151 | m_notFoundAssetUuids.Add(new UUID(thiskey)); |
152 | continue; | ||
153 | } | ||
171 | 154 | ||
172 | try | 155 | sbyte assetType = kvp.Value; |
173 | { | 156 | if (asset != null && assetType == (sbyte)AssetType.Unknown) |
174 | lock (this) | ||
175 | { | ||
176 | // Take care of the possibilty that this thread started but was paused just outside the lock before | ||
177 | // the final request came in (assuming that such a thing is possible) | ||
178 | if (m_requestState == RequestState.Completed) | ||
179 | { | 157 | { |
180 | timedOut = false; | 158 | m_log.InfoFormat("[ARCHIVER]: Rewriting broken asset type for {0} to {1}", thiskey, SLUtil.AssetTypeFromCode(assetType)); |
181 | return; | 159 | asset.Type = assetType; |
182 | } | 160 | } |
183 | |||
184 | m_requestState = RequestState.Aborted; | ||
185 | } | ||
186 | 161 | ||
187 | // Calculate which uuids were not found. This is an expensive way of doing it, but this is a failure | 162 | m_foundAssetUuids.Add(asset.FullID); |
188 | // case anyway. | 163 | m_assetsArchiver.WriteAsset(PostProcess(asset)); |
189 | List<UUID> uuids = new List<UUID>(); | ||
190 | foreach (UUID uuid in m_uuids.Keys) | ||
191 | { | ||
192 | uuids.Add(uuid); | ||
193 | } | 164 | } |
194 | 165 | ||
195 | foreach (UUID uuid in m_foundAssetUuids) | 166 | catch (Exception e) |
196 | { | ||
197 | uuids.Remove(uuid); | ||
198 | } | ||
199 | |||
200 | foreach (UUID uuid in m_notFoundAssetUuids) | ||
201 | { | 167 | { |
202 | uuids.Remove(uuid); | 168 | m_log.ErrorFormat("[ARCHIVER]: Execute failed with {0}", e); |
203 | } | ||
204 | |||
205 | m_log.ErrorFormat( | ||
206 | "[ARCHIVER]: Asset service failed to return information about {0} requested assets", uuids.Count); | ||
207 | |||
208 | int i = 0; | ||
209 | foreach (UUID uuid in uuids) | ||
210 | { | ||
211 | m_log.ErrorFormat("[ARCHIVER]: No information about asset {0} received", uuid); | ||
212 | |||
213 | if (++i >= MAX_UUID_DISPLAY_ON_TIMEOUT) | ||
214 | break; | ||
215 | } | 169 | } |
216 | |||
217 | if (uuids.Count > MAX_UUID_DISPLAY_ON_TIMEOUT) | ||
218 | m_log.ErrorFormat( | ||
219 | "[ARCHIVER]: (... {0} more not shown)", uuids.Count - MAX_UUID_DISPLAY_ON_TIMEOUT); | ||
220 | |||
221 | m_log.Error("[ARCHIVER]: Archive save aborted. PLEASE DO NOT USE THIS ARCHIVE, IT WILL BE INCOMPLETE."); | ||
222 | } | ||
223 | catch (Exception e) | ||
224 | { | ||
225 | m_log.ErrorFormat("[ARCHIVER]: Timeout handler exception {0}{1}", e.Message, e.StackTrace); | ||
226 | } | 170 | } |
227 | finally | ||
228 | { | ||
229 | if (timedOut) | ||
230 | WorkManager.RunInThread(PerformAssetsRequestCallback, true, "Archive Assets Request Callback"); | ||
231 | } | ||
232 | } | ||
233 | 171 | ||
234 | protected void PreAssetRequestCallback(string fetchedAssetID, object assetType, AssetBase fetchedAsset) | 172 | m_timeOutTimer.Dispose(); |
235 | { | 173 | int totalerrors = m_notFoundAssetUuids.Count + m_previousErrorsCount; |
236 | // Check for broken asset types and fix them with the AssetType gleaned by UuidGatherer | ||
237 | if (fetchedAsset != null && fetchedAsset.Type == (sbyte)AssetType.Unknown) | ||
238 | { | ||
239 | m_log.InfoFormat("[ARCHIVER]: Rewriting broken asset type for {0} to {1}", fetchedAsset.ID, SLUtil.AssetTypeFromCode((sbyte)assetType)); | ||
240 | fetchedAsset.Type = (sbyte)assetType; | ||
241 | } | ||
242 | 174 | ||
243 | AssetRequestCallback(fetchedAssetID, this, fetchedAsset); | 175 | if(m_timeout) |
244 | } | 176 | m_log.DebugFormat("[ARCHIVER]: Aborted because AssetService request timeout. Successfully added {0} assets", m_foundAssetUuids.Count); |
177 | else if(totalerrors == 0) | ||
178 | m_log.DebugFormat("[ARCHIVER]: Successfully added all {0} assets", m_foundAssetUuids.Count); | ||
179 | else | ||
180 | m_log.DebugFormat("[ARCHIVER]: Successfully added {0} assets ({1} of total possible assets requested were not found, were damaged or were not assets)", | ||
181 | m_foundAssetUuids.Count, totalerrors); | ||
245 | 182 | ||
246 | /// <summary> | 183 | PerformAssetsRequestCallback(m_timeout); |
247 | /// Called back by the asset cache when it has the asset | 184 | } |
248 | /// </summary> | 185 | |
249 | /// <param name="assetID"></param> | 186 | private void OnTimeout(object source, ElapsedEventArgs args) |
250 | /// <param name="asset"></param> | ||
251 | public void AssetRequestCallback(string id, object sender, AssetBase asset) | ||
252 | { | 187 | { |
253 | Culture.SetCurrentCulture(); | 188 | m_timeout = true; |
254 | |||
255 | try | ||
256 | { | ||
257 | lock (this) | ||
258 | { | ||
259 | //m_log.DebugFormat("[ARCHIVER]: Received callback for asset {0}", id); | ||
260 | |||
261 | m_requestCallbackTimer.Stop(); | ||
262 | |||
263 | if ((m_requestState == RequestState.Aborted) || (m_requestState == RequestState.Completed)) | ||
264 | { | ||
265 | m_log.WarnFormat( | ||
266 | "[ARCHIVER]: Received information about asset {0} while in state {1}. Ignoring.", | ||
267 | id, m_requestState); | ||
268 | |||
269 | return; | ||
270 | } | ||
271 | |||
272 | if (asset != null) | ||
273 | { | ||
274 | if (m_options.ContainsKey("verbose")) | ||
275 | m_log.InfoFormat("[ARCHIVER]: Writing asset {0}", id); | ||
276 | |||
277 | m_foundAssetUuids.Add(asset.FullID); | ||
278 | |||
279 | m_assetsArchiver.WriteAsset(PostProcess(asset)); | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | if (m_options.ContainsKey("verbose")) | ||
284 | m_log.InfoFormat("[ARCHIVER]: Recording asset {0} as not found", id); | ||
285 | |||
286 | m_notFoundAssetUuids.Add(new UUID(id)); | ||
287 | } | ||
288 | |||
289 | if (m_foundAssetUuids.Count + m_notFoundAssetUuids.Count >= m_repliesRequired) | ||
290 | { | ||
291 | m_requestState = RequestState.Completed; | ||
292 | |||
293 | m_log.DebugFormat( | ||
294 | "[ARCHIVER]: Successfully added {0} assets ({1} assets not found but these may be expected invalid references)", | ||
295 | m_foundAssetUuids.Count, m_notFoundAssetUuids.Count); | ||
296 | |||
297 | // We want to stop using the asset cache thread asap | ||
298 | // as we now need to do the work of producing the rest of the archive | ||
299 | WorkManager.RunInThread(PerformAssetsRequestCallback, false, "Archive Assets Request Callback"); | ||
300 | } | ||
301 | else | ||
302 | { | ||
303 | m_requestCallbackTimer.Start(); | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | catch (Exception e) | ||
308 | { | ||
309 | m_log.ErrorFormat("[ARCHIVER]: AssetRequestCallback failed with {0}", e); | ||
310 | } | ||
311 | } | 189 | } |
312 | 190 | ||
313 | /// <summary> | 191 | /// <summary> |
314 | /// Perform the callback on the original requester of the assets | 192 | /// Perform the callback on the original requester of the assets |
315 | /// </summary> | 193 | /// </summary> |
316 | protected void PerformAssetsRequestCallback(object o) | 194 | private void PerformAssetsRequestCallback(object o) |
317 | { | 195 | { |
196 | if(m_assetsRequestCallback == null) | ||
197 | return; | ||
318 | Culture.SetCurrentCulture(); | 198 | Culture.SetCurrentCulture(); |
319 | 199 | ||
320 | Boolean timedOut = (Boolean)o; | 200 | Boolean timedOut = (Boolean)o; |
@@ -330,7 +210,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver | |||
330 | } | 210 | } |
331 | } | 211 | } |
332 | 212 | ||
333 | protected AssetBase PostProcess(AssetBase asset) | 213 | private AssetBase PostProcess(AssetBase asset) |
334 | { | 214 | { |
335 | if (asset.Type == (sbyte)AssetType.Object && asset.Data != null && m_options.ContainsKey("home")) | 215 | if (asset.Type == (sbyte)AssetType.Object && asset.Data != null && m_options.ContainsKey("home")) |
336 | { | 216 | { |