* Refactor: Genericise request limit strategies and move to OpenSim.Framework.Communications.Limit
							parent
							
								
									56dfa4e6da
								
							
						
					
					
						commit
						8c0955321a
					
				| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) Contributors, http://opensimulator.org/
 | 
			
		||||
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *     * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *       notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *     * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *       notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *       documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *     * Neither the name of the OpenSim Project nor the
 | 
			
		||||
 *       names of its contributors may be used to endorse or promote products
 | 
			
		||||
 *       derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 | 
			
		||||
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace OpenSim.Framework.Communications.Limit
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Interface for strategies that can limit requests from the client.  Currently only used in the
 | 
			
		||||
    /// texture modules to deal with repeated requests for certain textures.  However, limiting strategies
 | 
			
		||||
    /// could be used with other requests.
 | 
			
		||||
    /// </summary>    
 | 
			
		||||
    public interface IRequestLimitStrategy<TId>
 | 
			
		||||
    {           
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Should the request be allowed?  If the id is not monitored, then the request is always allowed.
 | 
			
		||||
        /// Otherwise, the strategy criteria will be applied.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="id"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        bool AllowRequest(TId id);
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Has the request been refused just once?
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>False if the request has not yet been refused, or if the request has been refused more
 | 
			
		||||
        /// than once.</returns>
 | 
			
		||||
        bool IsFirstRefusal(TId id);
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Start monitoring for future AllowRequest calls.  If the id is already monitored, then monitoring
 | 
			
		||||
        /// continues.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="id"></param>
 | 
			
		||||
        void MonitorRequests(TId id);
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Is the id being monitored?
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="uuid"> </param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        bool IsMonitoringRequests(TId id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) Contributors, http://opensimulator.org/
 | 
			
		||||
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *     * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *       notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *     * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *       notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *       documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *     * Neither the name of the OpenSim Project nor the
 | 
			
		||||
 *       names of its contributors may be used to endorse or promote products
 | 
			
		||||
 *       derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 | 
			
		||||
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace OpenSim.Framework.Communications.Limit
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Limit requests by discarding them after they've been repeated a certain number of times.
 | 
			
		||||
    /// </summary>    
 | 
			
		||||
    public class RepeatLimitStrategy<TId> : IRequestLimitStrategy<TId>
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly log4net.ILog m_log 
 | 
			
		||||
            = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Record each asset request that we're notified about.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly Dictionary<TId, int> requestCounts = new Dictionary<TId, int>();
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The maximum number of requests that can be made before we drop subsequent requests.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly int m_maxRequests;
 | 
			
		||||
        public int MaxRequests
 | 
			
		||||
        {
 | 
			
		||||
            get { return m_maxRequests; }
 | 
			
		||||
        }
 | 
			
		||||
       
 | 
			
		||||
        /// <summary></summary>
 | 
			
		||||
        /// <param name="maxRequests">The maximum number of requests that may be served before all further
 | 
			
		||||
        /// requests are dropped.</param>
 | 
			
		||||
        public RepeatLimitStrategy(int maxRequests)
 | 
			
		||||
        {
 | 
			
		||||
            m_maxRequests = maxRequests;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <see cref="IRequestLimitStrategy"/>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool AllowRequest(TId id)
 | 
			
		||||
        {                 
 | 
			
		||||
            if (requestCounts.ContainsKey(id))
 | 
			
		||||
            {
 | 
			
		||||
                requestCounts[id] += 1;
 | 
			
		||||
                
 | 
			
		||||
                if (requestCounts[id] > m_maxRequests)
 | 
			
		||||
                {                    
 | 
			
		||||
                    return false;
 | 
			
		||||
                }                                                         
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <see cref="IRequestLimitStrategy"/>
 | 
			
		||||
        /// </summary>        
 | 
			
		||||
        public bool IsFirstRefusal(TId id)
 | 
			
		||||
        {
 | 
			
		||||
            if (m_maxRequests + 1 == requestCounts[id])
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }            
 | 
			
		||||
            
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <see cref="IRequestLimitStrategy"/>
 | 
			
		||||
        /// </summary>        
 | 
			
		||||
        public void MonitorRequests(TId id)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IsMonitoringRequests(id))
 | 
			
		||||
            {
 | 
			
		||||
                requestCounts.Add(id, 1);
 | 
			
		||||
            }            
 | 
			
		||||
        }  
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <see cref="IRequestLimitStrategy"/>
 | 
			
		||||
        /// </summary>         
 | 
			
		||||
        public bool IsMonitoringRequests(TId id)
 | 
			
		||||
        {
 | 
			
		||||
            return requestCounts.ContainsKey(id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ using libsecondlife;
 | 
			
		|||
using libsecondlife.Packets;
 | 
			
		||||
 | 
			
		||||
using OpenSim.Framework;
 | 
			
		||||
using OpenSim.Framework.Communications.Limit;
 | 
			
		||||
using OpenSim.Framework.Console;
 | 
			
		||||
using OpenSim.Region.Environment.Interfaces;
 | 
			
		||||
using OpenSim.Region.Environment.Scenes;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +54,20 @@ namespace OpenSim.Region.Environment.Modules
 | 
			
		|||
        /// </summary>
 | 
			
		||||
        private static readonly int MAX_ALLOWED_TEXTURE_REQUESTS = 5;
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// We're going to limit repeated requests for the same missing texture.
 | 
			
		||||
        /// XXX This is really a temporary solution to deal with the situation where a client continually requests
 | 
			
		||||
        /// the same missing textures
 | 
			
		||||
        /// </summary>        
 | 
			
		||||
        private readonly IRequestLimitStrategy<LLUUID> missingTextureLimitStrategy 
 | 
			
		||||
            = new RepeatLimitStrategy<LLUUID>(MAX_ALLOWED_TEXTURE_REQUESTS);        
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// XXX Also going to limit repeated requests for found textures.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly IRequestLimitStrategy<LLUUID> foundTextureLimitStrategy 
 | 
			
		||||
            = new RepeatLimitStrategy<LLUUID>(MAX_ALLOWED_TEXTURE_REQUESTS);        
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Holds texture senders before they have received the appropriate texture from the asset cache.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -64,18 +79,6 @@ namespace OpenSim.Region.Environment.Modules
 | 
			
		|||
        /// </summary>
 | 
			
		||||
        private readonly BlockingQueue<ITextureSender> m_sharedSendersQueue;
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// We're going to record when we get a request for a particular missing texture for each client
 | 
			
		||||
        /// XXX This is really a temporary solution to deal with the situation where a client continually requests
 | 
			
		||||
        /// the same missing textures
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly Dictionary<LLUUID, int> missingTextureRequestCounts = new Dictionary<LLUUID, int>();
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// XXX Also going to record all the textures found and dispatched
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private readonly Dictionary<LLUUID, int> dispatchedTextureRequestCounts = new Dictionary<LLUUID, int>();
 | 
			
		||||
        
 | 
			
		||||
        private readonly Scene m_scene;
 | 
			
		||||
        
 | 
			
		||||
        private readonly IClientAPI m_client;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,50 +112,21 @@ namespace OpenSim.Region.Environment.Modules
 | 
			
		|||
                        textureSender.UpdateRequest(e.DiscardLevel, e.PacketNumber);                        
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {                 
 | 
			
		||||
                        // If we've already told the client we're missing the texture, then don't ask the 
 | 
			
		||||
                        // asset server for it again - record the fact that it's missing instead.
 | 
			
		||||
                        // XXX This is to reduce (but not resolve) a current problem where some clients keep 
 | 
			
		||||
                        // requesting the same textures                        
 | 
			
		||||
                        if (missingTextureRequestCounts.ContainsKey(e.RequestedAssetID))
 | 
			
		||||
                    {       
 | 
			
		||||
                        if (!foundTextureLimitStrategy.AllowRequest(e.RequestedAssetID))
 | 
			
		||||
                        {
 | 
			
		||||
                            missingTextureRequestCounts[e.RequestedAssetID] += 1;
 | 
			
		||||
                            
 | 
			
		||||
                            if (missingTextureRequestCounts[e.RequestedAssetID] > MAX_ALLOWED_TEXTURE_REQUESTS)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (MAX_ALLOWED_TEXTURE_REQUESTS + 1 == missingTextureRequestCounts[e.RequestedAssetID])
 | 
			
		||||
                                {
 | 
			
		||||
                                    m_log.DebugFormat(
 | 
			
		||||
                                        "[USER TEXTURE DOWNLOAD SERVICE]: Dropping requests for notified missing texture {0} for {1} since we have received more than {2} requests",
 | 
			
		||||
                                        e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS);
 | 
			
		||||
                                }
 | 
			
		||||
                                
 | 
			
		||||
                                return;
 | 
			
		||||
                            }                                                         
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {          
 | 
			
		||||
                            // If we keep receiving requests for textures we've already served to the client,
 | 
			
		||||
                            // then stop sending them.  This is a short term approach approach to the problem
 | 
			
		||||
                            // of clients which keep requesting the same texture - the long term approach
 | 
			
		||||
                            // will be to treat the cause (and possibly more generally cap the request 
 | 
			
		||||
                            // queues as well/instead)
 | 
			
		||||
                            if (dispatchedTextureRequestCounts.ContainsKey(e.RequestedAssetID))
 | 
			
		||||
                        else if (!missingTextureLimitStrategy.AllowRequest(e.RequestedAssetID))                      
 | 
			
		||||
                        {
 | 
			
		||||
                            if (missingTextureLimitStrategy.IsFirstRefusal(e.RequestedAssetID))
 | 
			
		||||
                            {
 | 
			
		||||
                                dispatchedTextureRequestCounts[e.RequestedAssetID] += 1;                                
 | 
			
		||||
                                
 | 
			
		||||
                                if (dispatchedTextureRequestCounts[e.RequestedAssetID] > MAX_ALLOWED_TEXTURE_REQUESTS)
 | 
			
		||||
                                {
 | 
			
		||||
//                                    if (MAX_ALLOWED_TEXTURE_REQUESTS + 1 == dispatchedTextureRequestCounts[e.RequestedAssetID])
 | 
			
		||||
//                                    {
 | 
			
		||||
//                                        m_log.DebugFormat(
 | 
			
		||||
//                                            "[USER TEXTURE DOWNLOAD SERVICE]: Dropping further requests for dispatched/queued texture {0} for {1} since we have received more than {2} requests",
 | 
			
		||||
//                                            e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS);
 | 
			
		||||
//                                    }
 | 
			
		||||
                                    
 | 
			
		||||
                                    return;
 | 
			
		||||
                                }                               
 | 
			
		||||
                            }
 | 
			
		||||
                                m_log.DebugFormat(
 | 
			
		||||
                                    "[USER TEXTURE DOWNLOAD SERVICE]: Dropping requests for notified missing texture {0} for client {1} since we have received more than {1} requests",
 | 
			
		||||
                                    e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS);
 | 
			
		||||
                            }       
 | 
			
		||||
                            
 | 
			
		||||
                            return;                                                        
 | 
			
		||||
                        }
 | 
			
		||||
                
 | 
			
		||||
                        m_scene.AddPendingDownloads(1);
 | 
			
		||||
| 
						 | 
				
			
			@ -197,9 +171,9 @@ namespace OpenSim.Region.Environment.Modules
 | 
			
		|||
                    // Needs investigation.
 | 
			
		||||
                    if (texture == null || texture.Data == null)
 | 
			
		||||
                    {                        
 | 
			
		||||
                        if (!missingTextureRequestCounts.ContainsKey(textureID))
 | 
			
		||||
                        if (!missingTextureLimitStrategy.IsMonitoringRequests(textureID))
 | 
			
		||||
                        {
 | 
			
		||||
                            missingTextureRequestCounts.Add(textureID, 1);
 | 
			
		||||
                            missingTextureLimitStrategy.MonitorRequests(textureID);
 | 
			
		||||
 | 
			
		||||
                            m_log.DebugFormat(
 | 
			
		||||
                                "[USER TEXTURE DOWNLOAD SERVICE]: Queueing first TextureNotFoundSender for {0}, client {1}", 
 | 
			
		||||
| 
						 | 
				
			
			@ -216,11 +190,7 @@ namespace OpenSim.Region.Environment.Modules
 | 
			
		|||
                            textureSender.TextureReceived(texture);
 | 
			
		||||
                            EnqueueTextureSender(textureSender);
 | 
			
		||||
                            
 | 
			
		||||
                            // Record the fact that we've put this texture in for dispatch
 | 
			
		||||
                            if (!dispatchedTextureRequestCounts.ContainsKey(textureID))
 | 
			
		||||
                            {
 | 
			
		||||
                                dispatchedTextureRequestCounts.Add(textureID, 1);
 | 
			
		||||
                            }
 | 
			
		||||
                            foundTextureLimitStrategy.MonitorRequests(textureID);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue