What I understand is, that GameplayAbilities do not need to replicate to update GameplayAttributes across the network, since they don't influence attributes directly. Instead, this is the task of GameplayEffects.
So what is it that updates the GameplayAbilitySystem attribute values (FGameplayAttributeData) over the network:
do only attributes replicate?
or are the GameplayEffects sent only?
or both?
To give context: I have an attribute modification system where I need several “base values”. Those “base values” change very infrequently, while the “final value” changes often. There are two possibilities to do that with GAS
use a separate attribute for each of the “base values” and “final value” or
add additional float members to the attribute struct FGameplayAttributeData, for all “base values” and the “final value”
If only GE are sent over the network (and not attributes), I would go for (2), since the size of an attribute doesn't matter for bandwidth then.
Both are replicated!
GameplayAttributes are replicated with an OnRep function, which calls FActiveGameplayEffectsContainer::SetBaseAttributeValueFromReplication(), which - despite the name - will also updates the current value using the aggregators which are existing on the local machine. For that to work, also ...
GameplayEffects replicate (see also Unreal GAS: GameplayEffect: Difference between minimal and full replication).
So for saving bandwidth in the example given, it is more meaningful to use separate attributes for the “base values” (option (1)), since they are not updated very often. So when the “final value” changes, the constant “base values” won’t have to be replicated.
Related
I'm trying to create a note-taking like app that uses NSPersistentCloudKitContainer and core data.
The store uses the NSMergeByPropertyObjectTrumpMergePolicy, which is fine for almost every property. For example, if the name of a file is changed on two different devices, then it's fine to use the latest value.
The problem is that the note text cannot be overridden by the latest value if it's changed on two devices at once. It needs to be detected as a conflict so the user can choose which version they want to keep.
I can replicate the behavior by turning off wifi on one device and writing content, then writing content on a different device at the same time. When I turn the wifi back on, whichever device saved the changes last completely overrides the other device's text.
What I'd like to accomplish is detect when there is a conflict of text, then create a duplicate file called "Conflicted Copy." Bonus points if someone can tell me how Apple Notes magically merges text without ever creating a conflict. I really only need a simple solution though that prevents data loss.
Any help in the right direction would be appreciated!
The conflict arises when CoreData & CloudKit sync an object to the persistent store while a managed context has an updated version of the object that has not yet been saved.
Any merge policy, including a custom merge policy, is used to create a single object that will be stored in the persistent store, but you want to have both conflicting objects so that the user can choose one of them.
Thus automatic conflict resolution, including with a custom merge policy, cannot be applied.
Instead, use the default merge policy of type error. The docs say
If a save fails because of conflicting objects, you can find the IDs
of those objects in error’s userInfo dictionary. Use the
NSInsertedObjectsKey and NSUpdatedObjectsKey keys to extract the
object IDs.
This means, you can keep the properties of the not-yet-stored conflicting object in the managed context, and re-fetch the properties of the conflicting object from the persistent store. This will overwrite the conflicting in-context version and "resolve" the conflict, but you still have now the conflicting properties and can present them to the user.
If the user selects the version in the persistent store, you are done. Otherwise update the objects properties with the kept values as required and save the context.
PS: Here is for your information how to implement a custom merge policy, although it cannot be applied here, just because such info is difficult to find.
EDIT:
I can imagine 2 ways to have one merge policy for your "normal" objects, and the error policy for the text objects:
A merge policy is set for a managed context. So you could have one context with NSMergeByPropertyObjectTrumpMergePolicy, and another one with NSErrorMergePolicy. So if it is feasible to fetch most objects into the 1st context, but the text storing objects into the 2nd one, you could apply both conflict resolution strategies.
During conflict resolution, i.e. in func resolve(optimisticLockingConflicts…, one has to call super.resolve(optimisticLockingConflicts: (see my example implementation cited above). So if you set NSMergeByPropertyObjectTrumpMergePolicy to your context, but call super only for your "normal" objects, this merge policy would not be applied to your text objects, and you could handle the conflict by your own. Warning: As far as I know this is non-standard, and I am not sure what happens if you don't call super.
I am thinking about how to best represent a certain data structure efficiently in Swift using CoreData. What I need is work with accounts (like savings, earning etc.). So what would probably make sense is an account class, where each account instance might have multiple characteristics like e.g. ACCOUNT_TYPE_ID which do not change. The core however is the VALUE attribute, which would hold the value of the account at a certain point in time. The complex thing here is that this value obviously might change over time (lets say on a daily basis, abstracting for intra-day changes) and I would need to be able to get the value of each instance for any given date. E.g. I might have my savings_private for which I would want to get the value at each month-end. This value might have changed, but as well could stay the same for various days/months. How could this most efficiently (when it comes to used space but - and that is even more important - be able to access computationally efficient) be done with a CoreData Entity/Class? I was thinking about maybe always starting with zero and then only somehow save the changes plus the date of change and then have some method for the call which would add all changes up to a date parameter - but was curious about what a best-practice might be here, as I guess I am not the first one trying to solve this.
I'm having a hard time understanding the shape of the state that's derived applying that entity's events vs a projection of that entity's data.
Is an Aggregate's state ONLY used for determining whether or not a command can successfully be applied? Or should that state be usable in other ways?
An example - I have a Post entity for a standard blog post. I might have events like postCreated, postPublished, postUnpublished, etc. For my projections that I'll be persisting in my read tables, I need a projection for the base posts (which will include all posts, regardless of status, with lots of detail) as well as published_posts projection (which will only represent posts that are currently published with only the information necessary for rendering.
In the situation above, is my aggregate state ONLY supposed to be used to determine, for example, if a post can be published or unpublished, etc? If this is the case, is the shape of my state within the aggregate purely defined by what's required for these validations? For example, in my base post projection, I want to have a list of all users that have made a change to the post. In terms of validation for the aggregate/commands, I couldn't care less about the list of users that have made changes. Does that mean that this list should not be a part of my state within my aggregate?
TL;DR: yes - limit the "state" in the aggregate to that data that you choose to cache in support of data change.
In my aggregates, I distinguish two different ideas:
the history , aka the sequence of events that describes the changes in the lifetime of the aggregate
the cache, aka the data values we tuck away because querying the event history every time kind of sucks.
There's not a lot of value in caching results that we are never going to use.
One of the underlying lessons of CQRS is that we don't need aggregates everywhere
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. -- Evans, 2003
If we aren't changing the data, then we can safely work directly with immutable copies of the data.
The only essential purpose of the aggregate is to determine what events, if any, need to be applied to bring the aggregate's state in line with a command (if the aggregate can be brought so in line). All state that's not needed for that purpose can be offloaded to a read-side, which can be thought of as a remix of the event stream (with each read-side only maintaining the state it needs).
That said, there are in practice, reasons to use the aggregate state directly, with the primary one being a desire for a stronger consistency for the aggregate: CQRS is inherently eventually consistent. As with all questions of consistent updates, it's important to recognize that consistency isn't free and very often isn't even cheap; I tend to think of a project as having a consistency budget and I'm pretty miserly about spending it.
In your case, there's probably no reason to include the list of users changing a post in the aggregate state, unless e.g. there's something like "no single user can modify a given post more than n times".
I have a collection in my Cosmos database that I would like to observe for changes. I have many documents (official and unofficial) explaining how to do this. There is one thing though that I cannot get to work in a reliable way: how do I receive the same changes to multiple instances when I don't have any common reference for instance names?
What do I mean by this? Well, I'm running my work loads in a Kubernetes cluster (AKS). I have a variable number of instances within the cluster that should observe my collection. In order for change feeds to work properly, I have to have a unique instance name for each instance. The only candidate I have is the pod name. It's usually on the form of <deployment-name>-<random string>. E.g. pod-5f597c9c56-lxw5b.
If I use the pod name as instance name, all instances do not receive the same changes (which is my requirement), only one instance will receive the change (see https://learn.microsoft.com/en-us/azure/cosmos-db/change-feed-processor#dynamic-scaling). What I can do is to use the pod name as feed name instead, then all instances get the same changes. This is what I fear will bite me in the butt at some point; when peek into the lease container, I can see a set of documents per feed name. As pod names come and go (the random string part of the name), I fear the container will grow over time, generating a heap of garbage. I know Cosmos can handle huge work loads, but you know, I like to keep things tidy.
How can I keep this thing clean and tidy? I really don't want to invent (or reuse for that matter!) some protocol between my instances to vote for which instance gets which name out of a finite set of names.
One "simple" solution would be to build my own instance names, if AKS or Kubernetes held some "index" of some sort for my pods. I know stateful sets give me that, but I don't want to use stateful sets, as the pods themselves aren't really stateful (except for this particular aspect!).
There is a new Change Feed pull model (which is in preview at this time).
The differences are:
In your case, it looks like you don't need parallelization (you want all instances to receive everything). The important part would be to design a state storing model that can maintain the continuation tokens (or not, maybe you don't care to continue if a pod goes down and then restarts).
I would suggest that you proceed to use the pod name as unique ID. If you are concerned about sprawl of the data, you could monitor the container and devise a clean-up mechanism for the metadata.
In order to have at-least-once delivery, there is going to need to be metadata persisted somewhere to track items ACK-ed / position in a partition, etc. I suspect there could be a bit of work to get change feed processor to give you at-least-once delivery once you consider pod interruption/re-scheduling during data flow.
As another option Azure offers an implementation of checkpoint based message sharing from partitioned event hubs via EventProcessorClient. In EventProcessorClient, there is also a bit of metadata added to a storage account.
Since Mongo doesn't have transactions that can be used to ensure that nothing is committed to the database unless its consistent (non corrupt) data, if my application dies between making a write to one document, and making a related write to another document, what techniques can I use to remove the corrupt data and/or recover in some way?
The greater idea behind NoSQL was to use a carefully modeled data structure for a specific problem, instead of hitting every problem with a hammer. That is also true for transactions, which should be referred to as 'short-lived transactions', because the typical RDBMS transaction hardly helps with 'real', long-lived transactions.
The kind of transaction supported by RDBMSs is often required only because the limited data model forces you to store the data across several tables, instead of using embedded arrays (think of the typical invoice / invoice items examples).
In MongoDB, try to use write-heavy, de-normalized data structures and keep data in a single document which improves read speed, data locality and ensures consistency. Such a data model is also easier to scale, because a single read only hits a single server, instead of having to collect data from multiple sources.
However, there are cases where the data must be read in a variety of contexts and de-normalization becomes unfeasible. In that case, you might want to take a look at Two-Phase Commits or choose a completely different concurrency approach, such as MVCC (in a sentence, that's what the likes of svn, git, etc. do). The latter, however, is hardly a drop-in replacement for RDBMs, but exposes a completely different kind of concurrency to a higher level of the application, if not the user.
Thinking about this myself, I want to identify some categories of affects:
Your operation has only one database save (saving data into one document)
Your operation has two database saves (updates, inserts, or deletions), A and B
They are independent
B is required for A to be valid
They are interdependent (A is required for B to be valid, and B is required for A to be valid)
Your operation has more than two database saves
I think this is a full list of the general possibilities. In case 1, you have no problem - one database save is atomic. In case 2.1, same thing, if they're independent, they might as well be two separate operations.
For case 2.2, if you do A first then B, at worst you will have some extra data (B data) that will take up space in your system, but otherwise be harmless. In case 2.3, you'll likely have some corrupt data in the event of a catastrophic failure. And case 3 is just a composition of case 2s.
Some examples for the different cases:
1.0. You change a car document's color to 'blue'
2.1. You change the car document's color to 'red' and the driver's hair color to 'red'
2.2. You create a new engine document and add its ID to the car document
2.3.a. You change your car's 'gasType' to 'diesel', which requires changing your engine to a 'diesel' type engine.
2.3.b. Another example of 2.3: You hitch car document A to another car document B, A getting the "towedBy" property set to B's ID, and B getting the "towing" property set to A's ID
3.0. I'll leave examples of this to your imagination
In many cases, its possible to turn a 2.3 scenario into a 2.2 scenario. In the 2.3.a example, the car document and engine are separate documents. Lets ignore the possibility of putting the engine inside the car document for this example. Its both invalid to have a diesel engine and non-diesel gas and to have a non-diesel engine and diesel gas. So they both have to change. But it may be valid to have no engine at all and have diesel gas. So you could add a step that makes the whole thing valid at all points. First, remove the engine, then replace the gas, then change the type of the engine, and lastly add the engine back onto the car.
If you will get corrupt data from a 2.3 scenario, you'll want a way to detect the corruption. In example 2.3.b, things might break if one document has the "towing" property, but the other document doesn't have a corresponding "towedBy" property. So this might be something to check after a catastrophic failure. Find all documents that have "towing" but the document with the id in that property doesn't have its "towedBy" set to the right ID. The choices there would be to delete the "towing" property or set the appropriate "towedBy" property. They both seem equally valid, but it might depend on your application.
In some situations, you might be able to find corrupt data like this, but you won't know what the data was before those things were set. In those cases, setting a default is probably better than nothing. Some types of corruption are better than others (particularly the kind that will cause errors in your application rather than simply incorrect display data).
If the above kind of code analysis or corruption repair becomes unfeasible, or if you want to avoid any data corruption at all, your last resort would be to take mnemosyn's suggestion and implement Two-Phase Commits, MVCC, or something similar that allows you to identify and roll back changes in an indeterminate state.