split userprofiles request queues into local and HG, add console debug comand profiles status to check size of those queues
Some checks failed
.msbuildnet6 / build (push) Has been cancelled

This commit is contained in:
UbitUmarov
2026-02-27 01:33:50 +00:00
parent 492ac6a629
commit a99a3d2b3c

View File

@@ -45,7 +45,6 @@ using OpenSim.Services.Connectors.Hypergrid;
using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.UserProfilesService; using OpenSim.Services.UserProfilesService;
using GridRegion = OpenSim.Services.Interfaces.GridRegion; using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using OpenSim.Region.CoreModules.Avatar.Friends;
namespace OpenSim.Region.CoreModules.Avatar.UserProfiles namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
{ {
@@ -61,8 +60,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
// The pair of Dictionaries are used to handle the switching of classified ads // The pair of Dictionaries are used to handle the switching of classified ads
// by maintaining a cache of classified id to creator id mappings and an interest // by maintaining a cache of classified id to creator id mappings and an interest
// count. The entries are removed when the interest count reaches 0. // count. The entries are removed when the interest count reaches 0.
readonly Dictionary<UUID, UUID> m_classifiedCache = new(); readonly Dictionary<UUID, UUID> m_classifiedCache = [];
readonly Dictionary<UUID, int> m_classifiedInterest = new(); readonly Dictionary<UUID, int> m_classifiedInterest = [];
readonly ExpiringCacheOS<UUID, UserProfileCacheEntry> m_profilesCache = new(60000); readonly ExpiringCacheOS<UUID, UserProfileCacheEntry> m_profilesCache = new(60000);
IGroupsModule m_groupsModule = null; IGroupsModule m_groupsModule = null;
@@ -79,128 +78,150 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
public int reqtype; public int reqtype;
} }
private readonly ConcurrentStack<AsyncPropsRequest> m_asyncRequests = new(); private readonly ConcurrentStack<AsyncPropsRequest> m_asyncLocalRequests = new();
private readonly object m_asyncRequestsLock = new(); private readonly object m_asyncLocalRequestsLock = new();
private bool m_asyncRequestsRunning = false; private bool m_asyncLocalRequestsRunning = false;
private readonly ConcurrentStack<AsyncPropsRequest> m_asyncHGRequests = new();
private readonly object m_asyncHGRequestsLock = new();
private bool m_asyncHGRequestsRunning = false;
private void ProcessRequests() private void ProcessLocalRequests()
{ {
lock(m_asyncRequestsLock) lock(m_asyncLocalRequestsLock)
{ {
while (m_asyncRequests.TryPop(out AsyncPropsRequest req)) while (m_asyncLocalRequests.TryPop(out AsyncPropsRequest req))
{ {
try IClientAPI client = req.client;
if(!client.IsActive)
continue;
ProcessRequest(req);
}
m_asyncLocalRequestsRunning = false;
}
}
private void ProcessHGRequests()
{
lock(m_asyncHGRequestsLock)
{
while (m_asyncHGRequests.TryPop(out AsyncPropsRequest req))
{
IClientAPI client = req.client;
if(!client.IsActive)
continue;
ProcessRequest(req);
}
m_asyncHGRequestsRunning = false;
}
}
private void ProcessRequest(AsyncPropsRequest req)
{
try
{
IClientAPI client = req.client;
if(req.reqtype == 0)
{
ScenePresence p = req.presence;
bool foreign = GetUserProfileServerURI(req.agent, out string serverURI);
bool ok = serverURI.Length > 0;
byte[] membershipType = new byte[1];
string born = string.Empty;
uint flags = 0x00;
if (ok && GetUserAccountData(req.agent, out UserAccount acc))
{ {
IClientAPI client = req.client; flags = (uint)(acc.UserFlags & 0xff);
if(!client.IsActive)
continue;
if(req.reqtype == 0) if (acc.UserTitle.Length == 0)
membershipType[0] = (byte)((acc.UserFlags & 0x0f00) >> 8);
else
membershipType = Utils.StringToBytes(acc.UserTitle);
int val_born = acc.Created;
if (val_born != 0)
born = Util.ToDateTime(val_born).ToString("M/d/yyyy", CultureInfo.InvariantCulture);
}
else
ok = false;
UserProfileProperties props = new() { UserId = req.agent };
if (ok)
ok = GetProfileData(ref props, foreign, serverURI, out string result);
if (!ok)
props.AboutText = "Profile not available at this time. User may still be unknown to this grid";
if (!m_allowUserProfileWebURLs)
props.WebUrl = "";
GroupMembershipData[] agentGroups = null;
if(ok && m_groupsModule is not null)
agentGroups = m_groupsModule.GetMembershipData(req.agent);
HashSet<IClientAPI> clients;
lock (m_profilesCache)
{
if (!m_profilesCache.TryGetValue(props.UserId, out UserProfileCacheEntry uce) || uce is null)
uce = new UserProfileCacheEntry();
uce.props = props;
uce.born = born;
uce.membershipType = membershipType;
uce.flags = flags;
clients = uce.ClientsWaitingProps;
uce.ClientsWaitingProps = null;
uce.avatarGroups = agentGroups;
m_profilesCache.AddOrUpdate(props.UserId, uce, PROFILECACHEEXPIRE);
}
if (IsFriendOnline(req.client, req.agent))
flags |= (uint)ProfileFlags.Online;
else
flags &= (uint)~ProfileFlags.Online;
if (clients is null)
{
client.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
client.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
if (agentGroups is not null)
client.SendAvatarGroupsReply(req.agent, agentGroups);
}
else
{
if (!clients.Contains(client) && client.IsActive)
{ {
ScenePresence p = req.presence; client.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
bool foreign = GetUserProfileServerURI(req.agent, out string serverURI); client.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
bool ok = serverURI.Length > 0; (uint)props.SkillsMask, props.SkillsText, props.Language);
if (agentGroups is not null)
client.SendAvatarGroupsReply(req.agent, agentGroups);
}
foreach (IClientAPI cli in clients)
{
if (!cli.IsActive)
continue;
cli.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
byte[] membershipType = new byte[1]; cli.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
string born = string.Empty; (uint)props.SkillsMask, props.SkillsText, props.Language);
uint flags = 0x00; if (agentGroups is not null)
cli.SendAvatarGroupsReply(req.agent, agentGroups);
if (ok && GetUserAccountData(req.agent, out UserAccount acc))
{
flags = (uint)(acc.UserFlags & 0xff);
if (acc.UserTitle.Length == 0)
membershipType[0] = (byte)((acc.UserFlags & 0x0f00) >> 8);
else
membershipType = Utils.StringToBytes(acc.UserTitle);
int val_born = acc.Created;
if (val_born != 0)
born = Util.ToDateTime(val_born).ToString("M/d/yyyy", CultureInfo.InvariantCulture);
}
else
ok = false;
UserProfileProperties props = new() { UserId = req.agent };
if (ok)
ok = GetProfileData(ref props, foreign, serverURI, out string result);
if (!ok)
props.AboutText = "Profile not available at this time. User may still be unknown to this grid";
if (!m_allowUserProfileWebURLs)
props.WebUrl = "";
GroupMembershipData[] agentGroups = null;
if(ok && m_groupsModule is not null)
agentGroups = m_groupsModule.GetMembershipData(req.agent);
HashSet<IClientAPI> clients;
lock (m_profilesCache)
{
if (!m_profilesCache.TryGetValue(props.UserId, out UserProfileCacheEntry uce) || uce is null)
uce = new UserProfileCacheEntry();
uce.props = props;
uce.born = born;
uce.membershipType = membershipType;
uce.flags = flags;
clients = uce.ClientsWaitingProps;
uce.ClientsWaitingProps = null;
uce.avatarGroups = agentGroups;
m_profilesCache.AddOrUpdate(props.UserId, uce, PROFILECACHEEXPIRE);
}
if (IsFriendOnline(req.client, req.agent))
flags |= (uint)ProfileFlags.Online;
else
flags &= (uint)~ProfileFlags.Online;
if (clients is null)
{
client.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
client.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
if (agentGroups is not null)
client.SendAvatarGroupsReply(req.agent, agentGroups);
}
else
{
if (!clients.Contains(client) && client.IsActive)
{
client.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
client.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
if (agentGroups is not null)
client.SendAvatarGroupsReply(req.agent, agentGroups);
}
foreach (IClientAPI cli in clients)
{
if (!cli.IsActive)
continue;
cli.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType, props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
cli.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
if (agentGroups is not null)
cli.SendAvatarGroupsReply(req.agent, agentGroups);
}
}
} }
} }
catch (Exception e)
{
m_log.ErrorFormat("[UserProfileModule]: Process fail {0} : {1}", e.Message, e.StackTrace);
}
} }
m_asyncRequestsRunning = false; }
catch (Exception e)
{
m_log.Error($"[UserProfileModule]: Process fail {e.Message} : {e.StackTrace}");
} }
} }
@@ -243,7 +264,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
public bool Enabled public bool Enabled
{ {
get; get;
set; private set;
} }
private GridInfo m_thisGridInfo; private GridInfo m_thisGridInfo;
@@ -260,7 +281,6 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
public void Initialise(IConfigSource source) public void Initialise(IConfigSource source)
{ {
Config = source; Config = source;
ReplaceableInterface = typeof(IProfileModule);
IConfig profileConfig = Config.Configs["UserProfiles"]; IConfig profileConfig = Config.Configs["UserProfiles"];
@@ -292,10 +312,19 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
m_allowUserProfileWebURLs = profileConfig.GetBoolean("AllowUserProfileWebURLs", m_allowUserProfileWebURLs); m_allowUserProfileWebURLs = profileConfig.GetBoolean("AllowUserProfileWebURLs", m_allowUserProfileWebURLs);
m_log.Debug("[UserProfileModule]: Full Profiles Enabled"); m_log.Debug("[UserProfileModule]: Full Profiles Enabled");
ReplaceableInterface = null;
MainConsole.Instance.Commands.AddCommand("Debug", false, "profiles status",
"profiles status",
"Show user profile Queues count",
HandleShowStatus);
Enabled = true; Enabled = true;
} }
private void HandleShowStatus(string module, string[] cmdparms)
{
MainConsole.Instance.Output($"Profile requests in '{Scene.Name}' Local: {m_asyncLocalRequests.Count} HG: {m_asyncHGRequests.Count}");
}
/// <summary> /// <summary>
/// Adds the region. /// Adds the region.
/// </summary> /// </summary>
@@ -369,7 +398,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
/// </value> /// </value>
public Type ReplaceableInterface public Type ReplaceableInterface
{ {
get; private set; get { return null; }
} }
/// <summary> /// <summary>
@@ -1645,131 +1674,47 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
agent = avatarID, agent = avatarID,
reqtype = 0 reqtype = 0
}; };
m_asyncRequests.Push(req);
if (Monitor.TryEnter(m_asyncRequestsLock)) if(m_userManagementModule.IsLocalGridUser(avatarID))
{ {
try m_asyncLocalRequests.Push(req);
if (Monitor.TryEnter(m_asyncLocalRequestsLock))
{ {
if (!m_asyncRequestsRunning) try
{ {
m_asyncRequestsRunning = true; if (!m_asyncLocalRequestsRunning)
Util.FireAndForget(x => ProcessRequests()); {
m_asyncLocalRequestsRunning = true;
Util.FireAndForget(x => ProcessLocalRequests());
}
}
finally
{
Monitor.Exit(m_asyncLocalRequestsLock);
} }
} }
finally
{
Monitor.Exit(m_asyncRequestsLock);
}
}
/*
string serverURI = string.Empty;
bool foreign = GetUserProfileServerURI(avatarID, out serverURI);
UserAccount account = null;
Dictionary<string,object> userInfo;
if (!foreign)
{
account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, avatarID);
} }
else else
{ {
userInfo = new Dictionary<string, object>(); m_asyncHGRequests.Push(req);
}
Byte[] membershipType = new Byte[1]; if (Monitor.TryEnter(m_asyncHGRequestsLock))
string born = string.Empty;
uint flags = 0x00;
if (null != account)
{
if (account.UserTitle.Length == 0)
membershipType[0] = (Byte)((account.UserFlags & 0xf00) >> 8);
else
membershipType = Utils.StringToBytes(account.UserTitle);
born = Util.ToDateTime(account.Created).ToString(
"M/d/yyyy", CultureInfo.InvariantCulture);
flags = (uint)(account.UserFlags & 0xff);
}
else
{
if (GetUserAccountData(avatarID, out userInfo) == true)
{ {
if ((string)userInfo["user_title"].Length == 0) try
membershipType[0] = (Byte)(((Byte)userInfo["user_flags"] & 0xf00) >> 8); {
else if (!m_asyncHGRequestsRunning)
membershipType = Utils.StringToBytes((string)userInfo["user_title"]); {
m_asyncHGRequestsRunning = true;
int val_born = (int)userInfo["user_created"]; Util.FireAndForget(x => ProcessHGRequests());
if(val_born != 0) }
born = Util.ToDateTime(val_born).ToString( }
"M/d/yyyy", CultureInfo.InvariantCulture); finally
{
// picky, picky Monitor.Exit(m_asyncHGRequestsLock);
int val_flags = (int)userInfo["user_flags"]; }
flags = (uint)(val_flags & 0xff);
} }
} }
props = new UserProfileProperties();
props.UserId = avatarID;
string result = string.Empty;
if(!GetProfileData(ref props, foreign, serverURI, out result))
{
props.AboutText ="Profile not available at this time. User may still be unknown to this grid";
}
if(!m_allowUserProfileWebURLs)
props.WebUrl ="";
HashSet<IClientAPI> clients;
lock(m_profilesCache)
{
if(!m_profilesCache.TryGetValue(props.UserId, out uce) || uce == null)
uce = new UserProfileCacheEntry();
uce.props = props;
uce.born = born;
uce.membershipType = membershipType;
uce.flags = flags;
clients = uce.ClientsWaitingProps;
uce.ClientsWaitingProps = null;
m_profilesCache.AddOrUpdate(props.UserId, uce, PROFILECACHEEXPIRE);
}
// if on same region force online
if(p != null && !p.IsDeleted)
flags |= 0x10;
if(clients == null)
{
remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
}
else
{
if(!clients.Contains(remoteClient))
clients.Add(remoteClient);
foreach(IClientAPI cli in clients)
{
if(!cli.IsActive)
continue;
cli.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags,
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
cli.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText,
(uint)props.SkillsMask, props.SkillsText, props.Language);
}
}
*/
} }
/// <summary> /// <summary>