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. A good starting practice is to periodically confirm the meters by using a separate title to check meter usage for real-world-player activity. You can have multiple titles in Development Mode, so that you can create titles on the fly as you reach development stages where you want to checkpoint that usage, and then delete those titles after you're done.
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.
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.
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.
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.?
For games with more real-time requirements, such as needing to update the server-authoritative player state frequently, a better solution than frequent updates to the PlayFab-stored data is to use hosted game servers. In that model, you connect the player to a server on session start. The server reads all the needed data from the service for the player, and then hosts the simulation state for that player over time, with the client exchanging data with the server at whatever rate you need. The server then updates the "long term storage" in PlayFab with the latest data for the player, either at the end of a session, or every few minutes if your sessions are particularly long.
This is the model used by real-time multiplayer games. It's also a valid technique for single-player titles that require server-authoritative checks, though if that frequency is only a few times a minute per player, Azure Functions CloudScript could be the better option. The total CloudScript cost is easy to compute. It's the total gigabyte-seconds that you consume, with a minimum of 128 MB and 100 ms per execution, plus the normal calculations for any other service API calls the script uses. For example, if you have a total memory footprint, between your script code and variable usage in the script, of 250 MB, you would need to run that script for 4 seconds (across multiple users, most likely) to get to 1 GB/s. And within that script code, if you read or write Entity Objects, Title Data, User Data, etc., you'll need to calculate the spin that each of those calls has on the profile meters. Since hosted game server costs are dependent on how many servers you're running, and that in turns depends upon how many players can be hosted on a server at a time, it's not necessarily trivial to determine where the break-even point is between the two. But if you find that you need to call your scripts at a high frequency, and need to read and write data from the service each time, it's very likely that a game server solution is the better way to go.
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.
If the client does have the state information the next time the game is played, you can compare the timestamp locally to that of the data from the service and decide which to use, or even provide the player with the option to choose.
A player's profile information may be relevant to more than just themselves. In some games, you might need it to query cross-player, whether to spur competition or to directly influence the player experience through asynchronous friend interactions, challenges, and so on.
For competition, Leaderboards are an ideal way to generate that tension by sharing a subset of information about other players (often those close to the current player's score) via a single call to the service. Since it's possible to return other profile elements, such as statistics and tags, using the Profile View Constraints, you can present a rich set of information about these other players. But it's important to not iterate over the list of all players in the leaderboard, trying to read additional information from each, as this would rapidly multiply the total number of profile reads, driving up your costs.
For games that use cross-player data directly in the session, the most common optimization is to read only the information about the few specific players with whom the local player is interacting. For real-time action games, this is usually done on the server hosting the session. For others–such as games where players can attack each others' bases–the most common approaches are to either read all that data onto the local device or to send the local player only the subset of the other player's data that the local player should know. Games that do the former use server-side logic to evaluate the final results the client sends. Games that do the latter update the data iteratively over the course of the session with more data, when the local player should have access to it. But even then, they also use server-side logic to evaluate the final results. Again, the tipping point in deciding whether this should be done through script or on a hosted server is down to frequency. If it's more than a few times a minute, you're better off using a hosted server for the portion of the session that requires that data.
Economy and Inventory
Inventory, and economy in general, requires a different approach. There's no avoiding the fact that if a player is giving up something of real (money) or perceived (virtual currency or consumable containers) value, they have an implicit expectation that the transaction will be honored. For any game with in-app purchases, an increment to the profile meter for a purchase made by the player with real-world currency is a trivial cost. In many types of games, inventory updates are infrequent enough to pose little concern for greatly increasing your overall usage. But for those with more frequent inventory updates–even if you don't have in-game monetization–there are optimization tricks to help reduce your costs.
A best practice is to only think of inventory in the literal sense, as reasonably finite inventory. For example, in an incremental (or "idle") game, it can be tempting to think of each resource that the player acquires as an item, incrementing the total number each time the player purchases another of it. But that model breaks down rapidly, as you calculate the frequency with which players take those actions. Right away, you would have to deal with each player hitting the meter hundreds or even thousands of times in a session. For situations like this, it's better to think of those elements as User Data, and to update it to the service using the recommendations in the Profile section above.
When it comes to games that have higher rates of update to inventory, we're back to the question of data timeliness. For example, an action game might track on the number of bullets a player has, but the backend data for that "stack" of bullets doesn't need to be updated with every pull of the trigger, especially if the game state is managed in a hosted server. Stackables give you a performance and cost advantage, since you can represent many virtual instances of an item as a single actual instance with a count. You can often aggregate changes to stacks of items over time and update them at the end of a session, or periodically throughout the session. When updating a stack, it's a good idea to call Player Item Management - Modify Item Uses to check whether you can just change the count of the stack, rather than adding N instances of the item which must each be processed for addition to the stack, then cleaned up.
By their nature, certain game genres, such as collectible card games, do need to update the player inventory at a somewhat higher rate. But even here, there are still opportunities to aggregate inventory changes and reduce the total usage on the profile meters. For example, in games that use drop tables, the design frequently employs a container that has one or more "pulls" from each of several different drop tables. Typically, if these pulls are only an occasional action the incremental cost is small enough to not cause concern. But if players can collect many of those containers, and open multiples in a short period of time, you could provide an "open N" or even "open all" option. In that case, you use PlayFab CloudScript using Azure Functions or your custom game server to either query Player Item Management - Get Random Result Tables for the set of drop tables or call Player Item Management - Evaluate Random Result Table without generating the inventory items. You could then update the player inventory far more efficiently by only adding instances where needed, and then updating the count of item stacks for the rest.
Content and Configuration
Where the Profile is primarily about the player, Content and Configuration is primarily about the title. A number of title-level components, such as Push Notifications, email services, and Title News are all included in this meter. In addition though, this is the meter used to track on Entity File usage. Similarly, requests for the URLs for uploading and downloading CDN files are tracked on this meter, though it's important to clarify that CDN costs are separate, and are billed based on the total gigabytes downloaded as described in Content Delivery Network (CDN). The calculation of the Content and Configuration read and storage meters are similar to the Profile meters in that they're based on the size of the data, though obviously the units are significantly larger than 1 KB. For the Content and Configuration write meter, it's solely based on the total number of write operations, with each incrementing the meter by 1.
As a side note, the Entity File system is the recommended service for large data, whether it's associated with the title, a Group, or an individual player. For games with large data saves per player, the Entity File system is generally more cost effective; though do bear in mind that the frequency of those updates has an impact on usage that is tracked. It's best to optimize the number of Files you need against the frequency with which they need to be updated.
In terms of optimizing costs on the Content and Configuration meter, the most important thing to keep track of is the frequency with which your title must request its own rarely changing configuration data. Primarily this is Title Data, which is commonly used to manage aspects of your games that are the same across all users, such as game balance/tuning data, achievement definitions, localization data, and so on.
For games that use CloudScript, if you find that there's a dependency on this title-level data for each call to a script, you might want to consider "baking" that data directly into the script. You can do this by the simple expedient of defining it as hard-coded data in the script itself, or by using static variables, as a means of caching, to read the information from the service once per virtual machine (VM) instance. This way, the title-level data is loaded the first time any given VM runs your script and is re-used by that VM on subsequent executions. Three things to bear in mind here: first, the script is used by many different users, so nothing should be considered consistent for an individual user between executions. Second, the processing of the data must be considered stateless, since a single player could hit different machines for each call. And finally, you need to track on the age of that data and periodically re-load it, to make sure you have the latest version.
This is one of the major "expansion joints" of the PlayFab service, allowing you to run server-authoritative logic from a client device or a server, or even trigger it via PlayStream Rules (for example, when a player enters a Segment). As opposed to custom game server hosting, you're only paying for the gigabyte seconds (GB/s) that the script runs, with per-execution minimums of 128 MB and 100 ms. So if your script uses a total of 250 MB of space (between script code and data), it would need to run a total of 4 seconds–likely across multiple executions–to get to one GB/s.
Since the use cases for CloudScript generally revolve around taking actions on behalf of your players, we've covered the majority of what you should be thinking about for optimizations in the last two sections on profile and content/configuration meters. However, the total number of executions for a title is also tracked as part of metering CloudScript usage, so it's valuable to review how often you need to make calls to CloudScript on a per-player basis. For some games it might be significantly more cost efficient to use a hosted game server to have a "hot" data store for active players, rather than try to manage a data store in iterative calls to CloudScript.
This meter is associated with all the analytics capabilities of the PlayFab service, from event ingestion and export to Event History search and Data Explorer queries. Your usage here's influenced by how fast you need events processed, how much event data that you want to keep hosted "hot" (for queries in the Game Manager), and how much you use the service to evaluate your data, either through Data Explorer queries or visualization software you connect to your data directly. This meter is impacted by both in-game activity (events) and out-of-game activity (analytics).
Ultimately, this means that the costs on Insights are driven by how much event data you get from players and how much analytics processing you do on that data. In terms of best practices, the advice on events above applies to the former, while for the latter you can control your costs in two ways. First, and simplest, is that you can set the total storage in the Insights Management tab of the Game Manager for your title to control how much total event data you retain in PlayFab. Next, and in that same tab, you can set the performance level for your title. This determines the total amount of CPU resources allocated to your title, as well as how much data is stored "hot" for queries in the Event History. How much you need for each depends upon the needs of your data analytics team members, so it's best to review this with them to understand what your settings should be.
For information about Insights and how to use it, see What is PlayFab Insights.
For information about Insight best practices, see Best Practices & FAQ.
For hosted game servers specifically, you can minimize your costs by optimizing the number of server cores you have to have running at any given time to support as many players as possible.
If low ping times are important on those servers, you can choose which regions to run the servers. For most games, the largest concentrations of players are limited to certain key regions, but if you have a widely dispersed player population—particularly when you're in the long tail of your game—you'll need to weigh the cost of running servers in every region near your players versus the impact of longer ping times on the subset of players in areas with few players. One thing that can help with that is to make sure you're using our QOS service to choose which regions to put players in, and then determine what your cut-off is for the minimum number of players you need playing in a region for it to be viable.
To help get you through development without running up costs, we provide a significant number of free server hours in our hosting service, and our Party service is free for all Development Mode titles. It's also worth calling out that our Party service is also free for all use with Xbox Live signed-in players, and for titles in our Standard, Premium, and Enterprise tiers, we provide a generous allowance of connectivity, voice, and Cognitive Services (voice transcription and translation) at no additional cost, to help with 21st Century Communications and Video Accessibility Act (CVAA) compliance.
Managing your Live Game
That covers the fundamentals of the meters, but what should you be thinking about once your title is live? Managing your community of players primarily involves analytics (Insights, in the section above) and updates to Content and Configuration. In addition, most games need to engage with players outside their normal interactions with the game itself. From re-engagement campaigns (to entice players to come back), to community rewards for meta-game activity, and even to thanking players for their own community work outside the game, a common practice these days is to use LiveOps techniques to keep players highly engaged.
One key to this is making sure that you effectively target your segmentation to well-defined groups, not only to keep costs down, but to make sure your logic is processed quickly. Take, for instance, player re-engagement–getting players to come back after they've stopped playing for some period of time. A good way to approach this is to define several lapsed user time frames, perhaps players who haven't played for 3, 7, and 21 days. For each, you can take a different approach to re-engagement, starting with a simple "we miss you" message, and culminating in a "here's some free gold/energy/etc." message (coupled, of course, with automatically adding that to the player's account). For each of those, the recommended approach is to define a one-hour window based on the last time the player signed in to play the game. That way, you're not only targeting the last time they played, you're also keeping the set of players in the segment minimized, in order to make the operation as efficient as possible. Using a Scheduled Task that fires once every hour on each of those segments, you send out the messages and add any items or virtual currencies (VC) to the player inventories.
Ultimately, it's the features your game needs that determine its usage of a backend service like PlayFab. Really, it all boils down to one comprehensive question: what requirements do you have for those features? Whether you prioritize security, timeliness of data, level of player interaction/competition, or something else entirely, there are any number of factors that can push you towards a higher level of interaction with backend data. Being able to look at those requirements with a critical eye and discern which are hard needs and which aren't, is very much akin to optimization of any other code in your game. It's a matter of taking a careful look at where your resource utilization is high and deciding whether you truly need it to be, or if there are ways you can redesign that logic to reduce usage.
If the interaction is real-time, player-to-player, it's down to the complexity of that interaction. For most games of this type, hosted servers that manage the simulation state and only update the backend data at the end of a session (or every few minutes, if the sessions are long) are usually the best solution. But there are plenty of cases where interaction is fully trusted, such as co-operative games, or those played only with (or against) friends, minimizing the incentive to cheat. Still others have only minimal requirements for how the data for a session must be checked, allowing both players to simply submit their reports after each session, so that server-side checks can compare the two.
For any game without a real-time requirement, consider whether the player really needs up-to-the-second accuracy. In highly competitive games with strong community interaction, that may very well be the case. But for plenty of games, information that's a few minutes out of date doesn't impact the player experience.
As a service, PlayFab continues to evolve, and as it does we will continue to update our documentation to help guide you through managing your costs while making use of the services that power the features you need. If you have any suggestions or thoughts on how we can improve on this, feel free to reach out to our team via the Contact Us form on our main site. For feedback on how to optimize a feature you have in mind, or for any general technical questions on using PlayFab, you can contact our support team via the community forums or, if you're in any paid tier of service, by submitting a ticket in the PlayFab Game Manager (click on the ? in the upper right hand corner from any page of your title). Our partnership with our developer community provides us with the feedback we need to keep growing and to stay ahead of your needs, so we're always happy to hear from you!