* Change inventory async response deliver to deliver all items and folders at once, rather than each individual

* This is required in order to work towards eliminating some inventory race conditions and to better deal with situations where a grid inventory server is slow or not responding.
0.6.0-stable
Justin Clarke Casey 2008-04-11 18:13:10 +00:00
parent 9c5d0f50fa
commit a5176c2e2c
8 changed files with 152 additions and 90 deletions

View File

@ -118,64 +118,82 @@ namespace OpenSim.Framework.Communications.Cache
} }
} }
} }
/// <summary>
/// Callback invoked when the inventory is received from an async request to the inventory service
/// </summary>
/// <param name="userID"></param>
/// <param name="inventoryCollection"></param>
public void InventoryReceive(LLUUID userID, ICollection<InventoryFolderImpl> folders, ICollection<InventoryItemBase> items)
{
// FIXME: Exceptions thrown upwards never appear on the console. Could fix further up if these
// are simply being swallowed
try
{
foreach (InventoryFolderImpl folder in folders)
{
FolderReceive(userID, folder);
}
foreach (InventoryItemBase item in items)
{
ItemReceive(userID, item);
}
}
catch (Exception e)
{
m_log.ErrorFormat("[INVENTORY CACHE]: {0}", e);
}
}
/// <summary> /// <summary>
/// Callback invoked when a folder is received from an async request to the inventory service. /// Callback invoked when a folder is received from an async request to the inventory service.
/// </summary> /// </summary>
/// <param name="userID"></param> /// <param name="userID"></param>
/// <param name="folderInfo"></param> /// <param name="folderInfo"></param>
public void FolderReceive(LLUUID userID, InventoryFolderImpl folderInfo) private void FolderReceive(LLUUID userID, InventoryFolderImpl folderInfo)
{ {
// FIXME: Exceptions thrown upwards never appear on the console. Could fix further up if these
// are simply being swallowed
try
{
// m_log.DebugFormat( // m_log.DebugFormat(
// "[INVENTORY CACHE]: Received folder {0} {1} for user {2}", // "[INVENTORY CACHE]: Received folder {0} {1} for user {2}",
// folderInfo.name, folderInfo.folderID, userID); // folderInfo.name, folderInfo.folderID, userID);
if (userID == UserProfile.ID) if (userID == UserProfile.ID)
{
if (RootFolder == null)
{ {
if (RootFolder == null) if (folderInfo.ParentID == LLUUID.Zero)
{ {
if (folderInfo.ParentID == LLUUID.Zero) m_rootFolder = folderInfo;
{
m_rootFolder = folderInfo;
}
} }
else if (RootFolder.ID == folderInfo.ParentID) }
else if (RootFolder.ID == folderInfo.ParentID)
{
if (!RootFolder.SubFolders.ContainsKey(folderInfo.ID))
{ {
if (!RootFolder.SubFolders.ContainsKey(folderInfo.ID)) RootFolder.SubFolders.Add(folderInfo.ID, folderInfo);
{
RootFolder.SubFolders.Add(folderInfo.ID, folderInfo);
}
else
{
AddPendingFolder(folderInfo);
}
} }
else else
{ {
InventoryFolderImpl folder = RootFolder.HasSubFolder(folderInfo.ParentID); AddPendingFolder(folderInfo);
if (folder != null) }
}
else
{
InventoryFolderImpl folder = RootFolder.HasSubFolder(folderInfo.ParentID);
if (folder != null)
{
if (!folder.SubFolders.ContainsKey(folderInfo.ID))
{ {
if (!folder.SubFolders.ContainsKey(folderInfo.ID)) folder.SubFolders.Add(folderInfo.ID, folderInfo);
{
folder.SubFolders.Add(folderInfo.ID, folderInfo);
}
}
else
{
AddPendingFolder(folderInfo);
} }
} }
else
ResolvePendingFolders(folderInfo); {
AddPendingFolder(folderInfo);
}
} }
}
catch (Exception e) ResolvePendingFolders(folderInfo);
{
m_log.ErrorFormat("[INVENTORY CACHE] {0}", e);
} }
} }
@ -187,7 +205,7 @@ namespace OpenSim.Framework.Communications.Cache
/// </summary> /// </summary>
/// <param name="userID"></param> /// <param name="userID"></param>
/// <param name="folderInfo"></param> /// <param name="folderInfo"></param>
public void ItemReceive(LLUUID userID, InventoryItemBase itemInfo) private void ItemReceive(LLUUID userID, InventoryItemBase itemInfo)
{ {
if ((userID == UserProfile.ID) && (RootFolder != null)) if ((userID == UserProfile.ID) && (RootFolder != null))
{ {
@ -212,6 +230,11 @@ namespace OpenSim.Framework.Communications.Cache
} }
} }
/// <summary>
/// Add an item to the user's inventory
/// </summary>
/// <param name="userID"></param>
/// <param name="itemInfo"></param>
public void AddItem(LLUUID userID, InventoryItemBase itemInfo) public void AddItem(LLUUID userID, InventoryItemBase itemInfo)
{ {
if ((userID == UserProfile.ID) && HasInventory) if ((userID == UserProfile.ID) && HasInventory)
@ -221,6 +244,11 @@ namespace OpenSim.Framework.Communications.Cache
} }
} }
/// <summary>
/// Update an item in the user's inventory
/// </summary>
/// <param name="userID"></param>
/// <param name="itemInfo"></param>
public void UpdateItem(LLUUID userID, InventoryItemBase itemInfo) public void UpdateItem(LLUUID userID, InventoryItemBase itemInfo)
{ {
if ((userID == UserProfile.ID) && HasInventory) if ((userID == UserProfile.ID) && HasInventory)
@ -229,6 +257,12 @@ namespace OpenSim.Framework.Communications.Cache
} }
} }
/// <summary>
/// Delete an item from the user's inventory
/// </summary>
/// <param name="userID"></param>
/// <param name="item"></param>
/// <returns></returns>
public bool DeleteItem(LLUUID userID, InventoryItemBase item) public bool DeleteItem(LLUUID userID, InventoryItemBase item)
{ {
bool result = false; bool result = false;
@ -240,6 +274,7 @@ namespace OpenSim.Framework.Communications.Cache
m_commsManager.InventoryService.DeleteInventoryItem(userID, item); m_commsManager.InventoryService.DeleteInventoryItem(userID, item);
} }
} }
return result; return result;
} }
} }

View File

@ -97,7 +97,7 @@ namespace OpenSim.Framework.Communications.Cache
CachedUserInfo userInfo = GetUserDetails(userID); CachedUserInfo userInfo = GetUserDetails(userID);
if (userInfo != null) if (userInfo != null)
{ {
m_commsManager.InventoryService.RequestInventoryForUser(userID, userInfo.FolderReceive, userInfo.ItemReceive); m_commsManager.InventoryService.RequestInventoryForUser(userID, userInfo.InventoryReceive);
} }
else else
{ {
@ -118,6 +118,14 @@ namespace OpenSim.Framework.Communications.Cache
return null; return null;
} }
/// <summary>
/// Handle an inventory folder creation request from the client.
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="folderID"></param>
/// <param name="folderType"></param>
/// <param name="folderName"></param>
/// <param name="parentID"></param>
public void HandleCreateInventoryFolder(IClientAPI remoteClient, LLUUID folderID, ushort folderType, public void HandleCreateInventoryFolder(IClientAPI remoteClient, LLUUID folderID, ushort folderType,
string folderName, LLUUID parentID) string folderName, LLUUID parentID)
{ {
@ -189,6 +197,12 @@ namespace OpenSim.Framework.Communications.Cache
} }
} }
/// <summary>
/// Handle an inventory folder move request from the client.
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="folderID"></param>
/// <param name="parentID"></param>
public void HandleMoveInventoryFolder(IClientAPI remoteClient, LLUUID folderID, LLUUID parentID) public void HandleMoveInventoryFolder(IClientAPI remoteClient, LLUUID folderID, LLUUID parentID)
{ {
CachedUserInfo userProfile; CachedUserInfo userProfile;

View File

@ -26,21 +26,34 @@
*/ */
using System.Collections.Generic; using System.Collections.Generic;
using libsecondlife; using libsecondlife;
using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Communications.Cache;
namespace OpenSim.Framework.Communications namespace OpenSim.Framework.Communications
{ {
public delegate void InventoryFolderInfo(LLUUID userID, InventoryFolderImpl folderInfo); /// <summary>
/// Callback used when a user's inventory is received from the inventory service
/// </summary>
public delegate void InventoryReceiptCallback(LLUUID userId, ICollection<InventoryFolderImpl> folders, ICollection<InventoryItemBase> items);
//public delegate void InventoryFolderInfo(LLUUID userID, InventoryFolderImpl folderInfo);
public delegate void InventoryItemInfo(LLUUID userID, InventoryItemBase itemInfo); //public delegate void InventoryItemInfo(LLUUID userID, InventoryItemBase itemInfo);
/// <summary> /// <summary>
/// Defines all the operations one can perform on a user's inventory. /// Defines all the operations one can perform on a user's inventory.
/// </summary> /// </summary>
public interface IInventoryServices public interface IInventoryServices
{ {
void RequestInventoryForUser(LLUUID userID, InventoryFolderInfo folderCallBack, InventoryItemInfo itemCallBack); /// <summary>
/// Request the inventory for a user. This is an asynchronous operation that will call the callback when the
/// inventory has been received
/// </summary>
/// <param name="userID"></param>
/// <param name="callback"></param>
void RequestInventoryForUser(LLUUID userID, InventoryReceiptCallback callback);
/// <summary> /// <summary>
/// Add a new folder to the given user's inventory /// Add a new folder to the given user's inventory

View File

@ -158,8 +158,9 @@ namespace OpenSim.Framework.Communications
return false; return false;
} }
public abstract void RequestInventoryForUser(LLUUID userID, InventoryFolderInfo folderCallBack, // See IInventoryServices
InventoryItemInfo itemCallBack); public abstract void RequestInventoryForUser(LLUUID userID, InventoryReceiptCallback callback);
public abstract void AddNewInventoryFolder(LLUUID userID, InventoryFolderBase folder); public abstract void AddNewInventoryFolder(LLUUID userID, InventoryFolderBase folder);
public abstract void MoveExistingInventoryFolder(InventoryFolderBase folder); public abstract void MoveExistingInventoryFolder(InventoryFolderBase folder);
public abstract void AddNewInventoryItem(LLUUID userID, InventoryItemBase item); public abstract void AddNewInventoryItem(LLUUID userID, InventoryItemBase item);

View File

@ -32,6 +32,9 @@ using libsecondlife;
namespace OpenSim.Framework namespace OpenSim.Framework
{ {
/// <summary>
/// Used to serialize a whole inventory for transfer over the network.
/// </summary>
public class InventoryCollection public class InventoryCollection
{ {
public List<InventoryFolderBase> _folders; public List<InventoryFolderBase> _folders;

View File

@ -41,8 +41,7 @@ namespace OpenSim.Grid.InventoryServer
{ {
private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public override void RequestInventoryForUser(LLUUID userID, InventoryFolderInfo folderCallBack, public override void RequestInventoryForUser(LLUUID userID, InventoryReceiptCallback callback)
InventoryItemInfo itemCallBack)
{ {
} }

View File

@ -39,32 +39,38 @@ namespace OpenSim.Region.Communications.Local
/// </summary> /// </summary>
public class LocalInventoryService : InventoryServiceBase public class LocalInventoryService : InventoryServiceBase
{ {
public override void RequestInventoryForUser(LLUUID userID, InventoryFolderInfo folderCallBack, public override void RequestInventoryForUser(LLUUID userID, InventoryReceiptCallback callback)
InventoryItemInfo itemCallBack)
{ {
List<InventoryFolderBase> folders = GetInventorySkeleton(userID); List<InventoryFolderBase> skeletonFolders = GetInventorySkeleton(userID);
InventoryFolderImpl rootFolder = null; InventoryFolderImpl rootFolder = null;
ICollection<InventoryFolderImpl> folders = new List<InventoryFolderImpl>();
ICollection<InventoryItemBase> items = new List<InventoryItemBase>();
//need to make sure we send root folder first // Need to retrieve the root folder on the first pass
foreach (InventoryFolderBase folder in folders) foreach (InventoryFolderBase folder in skeletonFolders)
{ {
if (folder.ParentID == LLUUID.Zero) if (folder.ParentID == LLUUID.Zero)
{ {
rootFolder = RequestInventoryFolder(userID, folder, folderCallBack, itemCallBack); //rootFolder = RequestInventoryFolder(userID, folder, callback);
rootFolder = new InventoryFolderImpl(folder);
folders.Add(rootFolder);
} }
} }
if (rootFolder != null) if (rootFolder != null)
{ {
foreach (InventoryFolderBase folder in folders) foreach (InventoryFolderBase folder in skeletonFolders)
{ {
if (folder.ID != rootFolder.ID) if (folder.ID != rootFolder.ID)
{ {
RequestInventoryFolder(userID, folder, folderCallBack, itemCallBack); //RequestInventoryFolder(userID, folder, callback);
folders.Add(new InventoryFolderImpl(folder));
} }
} }
} }
callback(userID, folders, items);
} }
public override void AddNewInventoryFolder(LLUUID userID, InventoryFolderBase folder) public override void AddNewInventoryFolder(LLUUID userID, InventoryFolderBase folder)
@ -99,26 +105,5 @@ namespace OpenSim.Region.Communications.Local
return true; return true;
} }
} }
/// <summary>
/// Send the given inventory folder and its item contents back to the requester.
/// </summary>
/// <param name="userID"></param>
/// <param name="folder"></param>
private InventoryFolderImpl RequestInventoryFolder(LLUUID userID, InventoryFolderBase folder,
InventoryFolderInfo folderCallBack,
InventoryItemInfo itemCallBack)
{
InventoryFolderImpl newFolder = new InventoryFolderImpl(folder);
folderCallBack(userID, newFolder);
List<InventoryItemBase> items = RequestFolderItems(newFolder.ID);
foreach (InventoryItemBase item in items)
{
itemCallBack(userID, item);
}
return newFolder;
}
} }
} }

View File

@ -51,12 +51,11 @@ namespace OpenSim.Region.Communications.OGS1
#region IInventoryServices Members #region IInventoryServices Members
// See IInventoryServices // See IInventoryServices
public void RequestInventoryForUser(LLUUID userID, InventoryFolderInfo folderCallBack, public void RequestInventoryForUser(LLUUID userID, InventoryReceiptCallback callback)
InventoryItemInfo itemCallBack)
{ {
if (!m_RequestingInventory.ContainsKey(userID)) if (!m_RequestingInventory.ContainsKey(userID))
{ {
InventoryRequest request = new InventoryRequest(userID, folderCallBack, itemCallBack); InventoryRequest request = new InventoryRequest(userID, callback);
m_RequestingInventory.Add(userID, request); m_RequestingInventory.Add(userID, request);
RequestInventory(userID); RequestInventory(userID);
} }
@ -105,13 +104,17 @@ namespace OpenSim.Region.Communications.OGS1
InventoryFolderImpl rootFolder = null; InventoryFolderImpl rootFolder = null;
InventoryRequest request = m_RequestingInventory[userID]; InventoryRequest request = m_RequestingInventory[userID];
ICollection<InventoryFolderImpl> folders = new List<InventoryFolderImpl>();
ICollection<InventoryItemBase> items = new List<InventoryItemBase>();
foreach (InventoryFolderBase folder in response.Folders) foreach (InventoryFolderBase folder in response.Folders)
{ {
if (folder.ParentID == LLUUID.Zero) if (folder.ParentID == LLUUID.Zero)
{ {
InventoryFolderImpl newfolder = new InventoryFolderImpl(folder); rootFolder = new InventoryFolderImpl(folder);
rootFolder = newfolder; folders.Add(rootFolder);
request.FolderCallBack(userID, newfolder); //request.FolderCallBack(userID, newfolder);
} }
} }
@ -121,16 +124,20 @@ namespace OpenSim.Region.Communications.OGS1
{ {
if (folder.ID != rootFolder.ID) if (folder.ID != rootFolder.ID)
{ {
InventoryFolderImpl newfolder = new InventoryFolderImpl(folder); folders.Add(new InventoryFolderImpl(folder));
request.FolderCallBack(userID, newfolder); //request.FolderCallBack(userID, newfolder);
} }
} }
foreach (InventoryItemBase item in response.AllItems) foreach (InventoryItemBase item in response.AllItems)
{ {
request.ItemCallBack(userID, item); items.Add(item);
//request.ItemCallBack(userID, item);
} }
} }
request.Callback(userID, folders, items);
m_RequestingInventory.Remove(userID); m_RequestingInventory.Remove(userID);
} }
else else
@ -223,17 +230,22 @@ namespace OpenSim.Region.Communications.OGS1
#endregion #endregion
/// <summary>
/// Caches a pending inventory request that has yet to be satisfied by the inventory service
/// </summary>
public class InventoryRequest public class InventoryRequest
{ {
public LLUUID UserID; public LLUUID UserID;
public InventoryFolderInfo FolderCallBack; public InventoryReceiptCallback Callback;
public InventoryItemInfo ItemCallBack; //public InventoryFolderInfo FolderCallBack;
//public InventoryItemInfo ItemCallBack;
public InventoryRequest(LLUUID userId, InventoryFolderInfo folderCall, InventoryItemInfo itemCall) public InventoryRequest(LLUUID userId, InventoryReceiptCallback callback)
{ {
UserID = userId; UserID = userId;
FolderCallBack = folderCall; //FolderCallBack = folderCall;
ItemCallBack = itemCall; //ItemCallBack = itemCall;
Callback = callback;
} }
} }
} }