PlayFab consumption: best practices
With PlayFab’s consumption-based pricing model, you only pay for the actual service usage of your titles. But this raises an obvious question: how do you best optimize those titles to save money, while still implementing all the features you need? In this document, we dive into those details, and talk about best practices that will help you to plan ahead. Before you move your titles from Development Mode to Live, you can review the meter information in the Billing Summary tab of the Game Manager. There are six consumption meters: Events, Profile, Content and Configuration, CloudScript, Insights, and Multiplayer Services. The best way to begin optimizing a title is to look at each of the meters that it uses and see how it accumulates over time. This allows you to quickly derive some obvious improvements. For more information about meters, see Pricing Meters.Events
There are two types of events in PlayFab: PlayStream and Telemetry. PlayFab processes PlayStream events to check if they trigger PlayStream Actions you have defined, and to update User Segmentation. Some PlayStream events are automatically generated as a result of calls to service features such as authentication and statistic updates. Titles can generate their own custom events to extend this functionality. Telemetry events aren’t processed by PlayStream. These events are used for analytics, and they go straight to the data warehouse. Telemetry events aren’t generated automatically in PlayFab; they’re custom events generated by the title. For both event types, larger payloads of data increase the total amount of meter usage. You can reduce that overall usage by making sure you’re only sending the data you need. A rough guide is that any given event increments the meter in question by 1 for every 1 KB of data in the event body. To choose which event path best suits your usage, you should have a clear plan for how you intend to use each custom event that you generate. Events that are only needed for offline evaluation of the title should go the Telemetry route. This can help to significantly reduce cost, as the overage charges on Telemetry events are less than half that of PlayStream. If you’re using one of the pre-built SDKs, it’s important to remember that some analytics data on user behavior is generated automatically as described in the PlayStream Event Model reference. To turn off optional events, change the analytics setting in the Settings/Data Collection tab of your game in the PlayFab Game Manager. Similarly, while it’s a good idea to have the “generate PlayStream event” option turned on for your CloudScript executions during debugging, you should make sure to disable that before going live. In the case of PlayStream Action triggered CloudScripts, you can always turn them on later if you need to debug some behavior in the wild.Profile
The profile is effectively “everything about the player”, including common elements such as inventory, saved data, and statistics. It also contains the meta-information that you use to drive unique experiences through the LiveOps capabilities in PlayStream, such as the User Segmentation mentioned above. In addition, some title-level legacy features, such as Title Data, Catalogs, and Stores, are included in this meter, as their data implementation is effectively the same as User Data. Since this set of meters is driven by all player actions, and actions taken on players, it’s overwhelmingly the one that needs the most optimization planning. The primary contributing factors to the usage captured by the pricing meters is the amount of data that is read, stored, and written, and how frequently you call the APIs that generate data that is metered. For more information about the specific API calls that result in “spin” on these meters, see Pricing Meters. We’ll start with User Data and Statistics, since their usage patterns are similar, and then we’ll talk about player inventory management.Data and statistics
When evaluating your usage of User Data, the same approximation logic as for events applies–each 1 KB increases the meter count by 1–though keep in mind that each “element” of a call should be thought of as distinct for purposes of this calculation. Each key value pair in a call to update User Data is counted separately. For reads, this 1 KB calculation applies to the total data returned, regardless of the number of key value pairs. This means that writing 10 keys of 100 bytes each is 10 “ticks” of the profile write meter, since each key value pair write is a minimum of 1 KB, while a read of those 10 keys is 1 “tick”, since it’s a total of 1 KB. For more specific details on the usages, see Pricing meters. Statistics are slightly more complex, as they update profile and may also have an impact on leaderboards. As a general rule, you can think of each statistic updated as a full profile update while reads are based on the total size of the data read in each call. And since storage is priced per gigabyte (GB) and statistics typically consume only a few bytes each, we’ll focus on reads and writes. Since the total data size is important, optimizing it’s the first step. Fortunately, there are a wide range of well-known techniques for this–using enums or IDs in place of spelled-out text for items, packing large data as binary, or even using bitfields to pack data into smaller spaces. After that’s done, the next step is to carefully evaluate when you need to make those read and write calls. If you’re coming from PC or console development–especially single player games–this might be a new pattern. But in those environments, you must be careful about resource hits and garbage collection on the client device since those can cause degradation in performance at critical times. When your title uses resources that are in the cloud, it’s necessary to extend that logic to consider each resource hit as having a real cost, in addition to a performance cost. How do you determine the optimal frequency of reads and writes? Two of the most common concerns of developers who want to update data and statistics more frequently are protecting against cheating, and having timely information stored server-side either for player-to-player interactions or to prevent data loss if the game exits unexpectedly.Cheat protection
While it might seem like you need to constantly update your backend data to ensure essential security, frequent updates are rarely necessary if your game doesn’t require fully server-authoritative control of the session. To start, the fundamental question is how much security that you actually need. For some games, particularly those that monetize primarily through in-game advertisements and don’t have leaderboards, cheating isn’t much of a concern. In this case, trusting the data the client sends up is often a viable approach, since the security of that data isn’t really a concern. To identify cheating behaviors and decide how you want to handle them, we recommend that you implement processes to review the events, data, and statistics of your players. For other games in which you need to support the integrity of competition or protect the overall player experience, stronger security is more important. Depending on your specific requirements, certain approaches can reduce the frequency of data reads and writes in those situations. If your game isn’t real-time, one approach that we recommend is to aggregate information about the player’s game over some period of time–often a “round” of the game, taking several minutes–and then send that data to a script that determines whether it’s valid. The key elements to evaluate can be game-dependent, but some examples of common concepts to consider are:- How long has it been since the last session report, and how long does the client say that it played in the latest one?
- What scores did the player register, and are they reasonable for that player given their level, equipment, etc.?
Data timeliness
One common theme we’ve heard among developers with high profile write rates is that they’re concerned about the player losing progress. If the player exits the game before it can save and the local state can’t be used the next time the game is played, or that state needs to travel between devices, you want to have the PlayFab-stored state information for the player be as up-to-date as possible. For many games, you can address this issue by having a regular, infrequent heartbeat of updates to the service (every 15 minutes, for example). But including extra logic to lengthen or shorten that frequency can also help. For example:- Include an “important update” override that causes an immediate write, and resets the timer, if the player actively does something significant, so that it can’t be lost.
- If your game continues to progress with no player input, consider lengthening the heartbeat period if there has been no input for a long time. This is especially important for idle games, where players frequently leave them running overnight.
- Give players a way to force an update directly, such as a button in a user menu. In this case though, be sure to throttle the rate at which calls are actually made from the client device to PlayFab, so that a player hitting that button over and over isn’t generating a call each time.
