From a9e8bd59a377e7c7ce81e3693875467926dd7d4b Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 13 Feb 2012 19:38:22 -0800 Subject: Fix a race condition in the simian groups connector. When requests were too slow they would circumvent the cache (piling up on the network service and making the problem even worse). This condition happens frequently during permission checks. --- .../SimianGroupsServicesConnectorModule.cs | 71 +++++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs index 42008da..130513d 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs @@ -30,6 +30,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Reflection; +using System.Threading; using Nwc.XmlRpc; @@ -167,6 +168,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private bool m_debugEnabled = false; + private Dictionary m_pendingRequests = new Dictionary(); + private ExpiringCache m_memoryCache; private int m_cacheTimeout = 30; @@ -1348,6 +1351,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Immediately forward the request if the cache is disabled. if (m_cacheTimeout == 0) { + m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: cache is disabled"); return WebUtil.PostToService(m_groupsServerURI, requestArgs); } @@ -1355,6 +1359,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (requestArgs["RequestMethod"] == "RemoveGeneric" || requestArgs["RequestMethod"] == "AddGeneric") { + m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: clearing generics cache"); + // Any and all updates cause the cache to clear m_memoryCache.Clear(); @@ -1366,18 +1372,67 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Create the cache key for the request and see if we have it cached string CacheKey = WebUtil.BuildQueryString(requestArgs); - OSDMap response = null; - if (!m_memoryCache.TryGetValue(CacheKey, out response)) + + // This code uses a leader/follower pattern. On a cache miss, the request is added + // to a queue; the first thread to add it to the queue completes the request while + // follow on threads busy wait for the results, this situation seems to happen + // often when checking permissions + while (true) { - // if it wasn't in the cache, pass the request to the Simian Grid Services - response = WebUtil.PostToService(m_groupsServerURI, requestArgs); + OSDMap response = null; + bool firstRequest = false; - // and cache the response - m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); + lock (m_memoryCache) + { + if (m_memoryCache.TryGetValue(CacheKey, out response)) + return response; + + if (! m_pendingRequests.ContainsKey(CacheKey)) + { + m_pendingRequests.Add(CacheKey,true); + firstRequest = true; + } + } + + if (firstRequest) + { + // if it wasn't in the cache, pass the request to the Simian Grid Services + try + { + response = WebUtil.PostToService(m_groupsServerURI, requestArgs); + } + catch (Exception e) + { + m_log.InfoFormat("[SIMIAN GROUPS CONNECTOR] request failed {0}",CacheKey); + } + + // and cache the response + lock (m_memoryCache) + { + m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); + m_pendingRequests.Remove(CacheKey); + } + + return response; + } + + Thread.Sleep(50); // waiting for a web request to complete, 50msecs is reasonable } - // return cached response - return response; + // if (!m_memoryCache.TryGetValue(CacheKey, out response)) + // { + // m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: query not in the cache"); + // Util.PrintCallStack(); + + // // if it wasn't in the cache, pass the request to the Simian Grid Services + // response = WebUtil.PostToService(m_groupsServerURI, requestArgs); + + // // and cache the response + // m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); + // } + + // // return cached response + // return response; } #endregion -- cgit v1.1 From 2ebb421331c4e6c4f57e0cc1bab79cea70937172 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Tue, 14 Feb 2012 17:20:34 -0800 Subject: Refactor appearance saving for NPC to use AvatarFactoryModule interface. --- .../Avatar/AvatarFactory/AvatarFactoryModule.cs | 40 +++++++++++++++++----- .../Framework/Interfaces/IAvatarFactoryModule.cs | 1 + .../Region/OptionalModules/World/NPC/NPCModule.cs | 12 +++---- 3 files changed, 38 insertions(+), 15 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 8d503bd..c7f4c20 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -111,6 +111,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory #region IAvatarFactoryModule + /// + /// + /// + /// + public void SetAppearance(IScenePresence sp, AvatarAppearance appearance) + { + SetAppearance(sp, appearance.Texture, appearance.VisualParams); + } + /// /// Set appearance data (texture asset IDs and slider settings) /// @@ -156,14 +165,23 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory changed = sp.Appearance.SetTextureEntries(textureEntry) || changed; // WriteBakedTexturesReport(sp, m_log.DebugFormat); - if (!ValidateBakedTextureCache(sp)) + + // If bake textures are missing and this is not an NPC, request a rebake from client + if (!ValidateBakedTextureCache(sp) && (((ScenePresence)sp).PresenceType != PresenceType.Npc)) RequestRebake(sp, true); // This appears to be set only in the final stage of the appearance // update transaction. In theory, we should be able to do an immediate // appearance send and save here. } - + + // NPC should send to clients immediately and skip saving appearance + if (((ScenePresence)sp).PresenceType == PresenceType.Npc) + { + SendAppearance((ScenePresence)sp); + return; + } + // save only if there were changes, send no matter what (doesn't hurt to send twice) if (changed) QueueAppearanceSave(sp.ControllingClient.AgentId); @@ -174,6 +192,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString()); } + private void SendAppearance(ScenePresence sp) + { + // Send the appearance to everyone in the scene + sp.SendAppearanceToAllOtherAgents(); + + // Send animations back to the avatar as well + sp.Animator.SendAnimPack(); + } + public bool SendAppearance(UUID agentId) { // m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId); @@ -185,12 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory return false; } - // Send the appearance to everyone in the scene - sp.SendAppearanceToAllOtherAgents(); - - // Send animations back to the avatar as well - sp.Animator.SendAnimPack(); - + SendAppearance(sp); return true; } @@ -626,4 +648,4 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt"); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs index 39a760c..34aca33 100644 --- a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs @@ -35,6 +35,7 @@ namespace OpenSim.Region.Framework.Interfaces public interface IAvatarFactoryModule { + void SetAppearance(IScenePresence sp, AvatarAppearance appearance); void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams); /// diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index dc6eefc..5359354 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -96,15 +96,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC if (!m_avatars.ContainsKey(agentId)) return false; + // Delete existing sp attachments scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false); - AvatarAppearance npcAppearance = new AvatarAppearance(appearance, true); - sp.Appearance = npcAppearance; + // Set new sp appearance. Also sends to clients. + scene.RequestModuleInterface().SetAppearance(sp, new AvatarAppearance(appearance, true)); + + // Rez needed sp attachments scene.AttachmentsModule.RezAttachments(sp); - - IAvatarFactoryModule module = scene.RequestModuleInterface(); - module.SendAppearance(sp.UUID); - + return true; } -- cgit v1.1