DC
DC7d ago

Best practise for clearing realtimearray

I understand that items from realtime array cannot be removed and to clear it I need create a new one. I am using realtime array to send "commands" from a host to a client and need data consistency of the command and it parameters. Since I send one command at a time, I figuire I could create the realtime array each time I want to send the command - is this reasonable, or would I encounter a performance problem?
11 Replies
kapa.ai
kapa.ai7d ago
Hi @DC. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the knowledge sources provided, I can offer some insights on using RealtimeArray in NormalVR, but there isn't specific information about the best practices for clearing a RealtimeArray. However, I can provide some relevant information and suggestions based on the available documentation. You are correct that RealtimeArray does not support removing individual items. According to the Collections documentation, RealtimeArray "only supports adding elements" and "removing items by index is not supported." Your approach of creating a new RealtimeArray each time you want to send a command is a reasonable workaround, especially if you're only sending one command at a time. This method would ensure data consistency for the command and its parameters. However, there are a few considerations: 1. Performance: Creating new objects frequently can impact performance, especially in Unity. The documentation doesn't provide specific performance metrics for creating RealtimeArrays, so it's difficult to say definitively whether this would cause performance issues. 2. Alternative approaches: You might want to consider using a different collection type that better suits your needs. For example: - StringKeyDictionary allows you to remove items and is transactional, which could help ensure data consistency. - RealtimeDictionary allows you to remove items, though it's non-transactional. 3. Clearing alternative: If you need to stick with RealtimeArray, another approach could be to keep a single RealtimeArray and use a separate property to indicate which command is currently active. This way, you're not creating new arrays constantly, but clients would need to check this property to know which command to process. Given the limited information in the knowledge sources about best practices for clearing RealtimeArray, I would recommend testing your approach in a small-scale prototype to evaluate its performance and reliability. If you encounter issues, consider exploring the alternative collection types mentioned above. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7d ago
<@1259730396577529917> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
DC
DCOP7d ago
@kapa.ai if I do option three wont it end up consuming memory over time?
kapa.ai
kapa.ai7d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're right to be concerned about memory consumption over time. Let's consider the implications of option three more closely based on the information we have from the NormalVR documentation. Option three, which involves keeping a single RealtimeArray and using a separate property to indicate the active command, could indeed lead to increased memory usage over time if not managed properly. The Collections documentation states that RealtimeArray "only supports adding elements" and doesn't support removal of individual items. This means that if you keep adding commands to the RealtimeArray without any way to remove them, the array will grow indefinitely, potentially consuming more memory over time. This could become problematic, especially in long-running sessions or if commands are sent frequently. To mitigate this issue while still using a single RealtimeArray, you might consider a few strategies: 1. Limit the array size: Implement a maximum size for the array. When this limit is reached, you could start overwriting older commands. 2. Periodic reset: At certain intervals or under specific conditions, you could create a new RealtimeArray to replace the old one, effectively "clearing" it. 3. Use indices cyclically: Instead of continuously adding new elements, you could use a fixed-size array and cycle through indices, overwriting old commands. However, these strategies add complexity and may not be ideal for ensuring data consistency, which you mentioned is important for your use case. Given these considerations, your original idea of creating a new RealtimeArray for each command might actually be a cleaner and more reliable solution, despite the potential performance overhead of object creation. It ensures a fresh state for each command and avoids the accumulation of old data. If memory and performance become issues with either approach, you might want to reconsider using one of the other collection types mentioned earlier, like StringKeyDictionary or RealtimeDictionary, which support removal of items and might be better suited for your command-sending scenario. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7d ago
<@1259730396577529917> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
DC
DCOP7d ago
@kapa.ai tell me why RealtimeDictionary is not transactional? If I create an object with X number of properties, then add the object to the dictionary, wont all the properties of the objecty be preserved, making it "transactional"?
kapa.ai
kapa.ai7d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- To answer your question about RealtimeDictionary and its non-transactional nature, let's refer to the NormalVR documentation. According to the Collections documentation, RealtimeDictionary is classified as a non-transactional collection. The documentation states: "Non-transactional collections work like RealtimeModels in that, first, changes are reflected instantly, and second, the server merges changes using a last-client-wins approach." This means that when you add an object to a RealtimeDictionary, the changes are applied immediately without waiting for server confirmation. While all properties of the object you add will be preserved, the "non-transactional" aspect refers to how conflicts are handled and how changes are applied across the network. The key points to understand are: 1. Instant reflection of changes: When you add an object to the dictionary, the change is reflected immediately on the client that made the change, without waiting for server confirmation. 2. Conflict resolution: If multiple clients make changes simultaneously, the server uses a "last-client-wins" approach to resolve conflicts. This means that if two clients try to add or modify an object with the same key at nearly the same time, the last change to reach the server will be the one that's applied. 3. No rejection of changes: Unlike transactional collections (like StringKeyDictionary), RealtimeDictionary doesn't reject changes if another client has modified the same key before your change is received by the server. 4. No versioning: RealtimeDictionary doesn't maintain versions for each key or the collection as a whole, which is a feature of transactional collections used to ensure consistency. So, while the properties of the object you add will be preserved, the "non-transactional" nature refers to how these changes are synchronized across the network and how conflicts are resolved. If you need stricter control over how changes are applied and how conflicts are handled, especially in scenarios where data consistency is crucial, you might want to consider using a transactional collection like StringKeyDictionary instead. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7d ago
<@1259730396577529917> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
DC
DCOP7d ago
@kapa.ai show me an example relatimemodel and realtiomecomponent that uses a StringKeyDictionary
kapa.ai
kapa.ai7d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! I'll provide you with an example of a RealtimeModel and RealtimeComponent that uses a StringKeyDictionary based on the NormalVR documentation. However, please note that the knowledge sources don't provide a complete, ready-to-use example. I'll construct one based on the information available, following the patterns and practices described in the documentation. First, let's create a RealtimeModel with a StringKeyDictionary:
using Normal.Realtime;
using Normal.Realtime.Serialization;
using Normal.Realtime.Collections;

[RealtimeModel]
public partial class PlayerScoresModel
{
[RealtimeProperty(1, true)]
private StringKeyDictionary<int> _scores;
}
using Normal.Realtime;
using Normal.Realtime.Serialization;
using Normal.Realtime.Collections;

[RealtimeModel]
public partial class PlayerScoresModel
{
[RealtimeProperty(1, true)]
private StringKeyDictionary<int> _scores;
}
Now, let's create a RealtimeComponent that uses this model:
using UnityEngine;
using Normal.Realtime;

public class PlayerScoresComponent : RealtimeComponent<PlayerScoresModel>
{
private void Awake()
{
// Initialize if needed
}

protected override void OnRealtimeModelReplaced(PlayerScoresModel previousModel, PlayerScoresModel currentModel)
{
if (previousModel != null)
{
// Unsubscribe from events on the previous model
previousModel.scores.didInsertModelForKey -= ScoreAdded;
previousModel.scores.didRemoveModelForKey -= ScoreRemoved;
}

if (currentModel != null)
{
// Subscribe to events on the new model
currentModel.scores.didInsertModelForKey += ScoreAdded;
currentModel.scores.didRemoveModelForKey += ScoreRemoved;
}
}
public void AddScore(string playerName, int score)
{
model.scores.Insert(playerName, score, (success) =>
{
if (success)
{
Debug.Log($"Score added for {playerName}");
}
else
{
Debug.LogError($"Failed to add score for {playerName}");
}
});
}

public void RemoveScore(string playerName)
{
model.scores.Remove(playerName, (success) =>
{
if (success)
{
Debug.Log($"Score removed for {playerName}");
}
else
{
Debug.LogError($"Failed to remove score for {playerName}");
}
});
}

private void ScoreAdded(StringKeyDictionary<int> dictionary, string key, int value)
{
Debug.Log($"Score added: {key} = {value}");
}

private void ScoreRemoved(StringKeyDictionary<int> dictionary, string key, int value)
{
Debug.Log($"Score removed: {key} = {value}");
}
}
using UnityEngine;
using Normal.Realtime;

public class PlayerScoresComponent : RealtimeComponent<PlayerScoresModel>
{
private void Awake()
{
// Initialize if needed
}

protected override void OnRealtimeModelReplaced(PlayerScoresModel previousModel, PlayerScoresModel currentModel)
{
if (previousModel != null)
{
// Unsubscribe from events on the previous model
previousModel.scores.didInsertModelForKey -= ScoreAdded;
previousModel.scores.didRemoveModelForKey -= ScoreRemoved;
}

if (currentModel != null)
{
// Subscribe to events on the new model
currentModel.scores.didInsertModelForKey += ScoreAdded;
currentModel.scores.didRemoveModelForKey += ScoreRemoved;
}
}
public void AddScore(string playerName, int score)
{
model.scores.Insert(playerName, score, (success) =>
{
if (success)
{
Debug.Log($"Score added for {playerName}");
}
else
{
Debug.LogError($"Failed to add score for {playerName}");
}
});
}

public void RemoveScore(string playerName)
{
model.scores.Remove(playerName, (success) =>
{
if (success)
{
Debug.Log($"Score removed for {playerName}");
}
else
{
Debug.LogError($"Failed to remove score for {playerName}");
}
});
}

private void ScoreAdded(StringKeyDictionary<int> dictionary, string key, int value)
{
Debug.Log($"Score added: {key} = {value}");
}

private void ScoreRemoved(StringKeyDictionary<int> dictionary, string key, int value)
{
Debug.Log($"Score removed: {key} = {value}");
}
}
This example demonstrates: 1. A PlayerScoresModel with a StringKeyDictionary to store player scores. 2. A PlayerScoresComponent that uses this model to add and remove scores. 3. The use of the Insert and Remove methods of StringKeyDictionary, which are transactional and use completion handlers. 4. Subscription to the didInsertModelForKey and didRemoveModelForKey events to react to changes. Remember, as mentioned in the Collections documentation, StringKeyDictionary is transactional, meaning changes are not reflected until confirmed by the server, and they can be rejected if there's a conflict. This example should give you a starting point for using StringKeyDictionary in your NormalVR project. You may need to adjust it based on your specific requirements and the overall structure of your application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7d ago
<@1259730396577529917> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?