Elenesski Object Database  EODB and NEODB
The Memento Pattern Database for Unity
32 Network - Understanding Latency

With NEODB you want to minimize the number of round-trips to the server, however, latency is more complicated than simple network latency. Failing to understand the three factors can result in an app that does not perform well and in some cases result in an app people don't use because it's too slow.

Latency = Network Latency + Data Latency + Processing Latency

For the best performance, you'll want to reduce the number of calls you make to the server when doing updates, however, finding data is where you'll encounter the most performance issues, so NEODB offers 133 different ways to search indexes and return data.

EODB and NEODB offer intelligent ways to initialize indexes to make searching extremely efficient. It's also possible to format index and parse index data instead of retrieving entire objects to data I/O and save processing time. There are options for sorting, limiting datasets and paging data so that you grab the middle of the dataset.

In terms of speed, ranked slowest to fastest is:

  • data latency (database I/Os) are the slowest
  • network latency is a bit faster
  • CPU processing is generally the fastest.

However, without thinking about it and designing anything the 3 latencies will combine to result in really slow games. It might even be enough to kill your app/game.

Network Latency

Network Latency is the actual time for your bits to travel along network wires from your game/app to the server, or vice versa. It's a combination of transmission time and packet acknowledgement time.

Transmitting data is done with packets. Each byte requires 10 bits to transmit, the extra two bits are for checksum and parity, as a quick check whether the byte was received successfully. If any byte within the packet fails, the entire packet has to be resent. (Noisy transmission lines can produce lots of errors resulting in slower internet connections; because data is sent and resent until errors are resolved. In general, these error occurs between the client and their ISP and not on the high-speed connections between major centres and the connections to ISP's hosting servers.)

If packets are 256 bytes and you have 4K of raw data to transmit, you need 20 packets to transmit it:

  • 4096 bytes * 10 bits/byte = 40960 bits/8 bits per byte = 5120 bytes / 256 bytes per packet = 20

Each time a packet is sent, it must be acknowledged, the ACK. ACK performs the parity and checksum test then transmits an OK or RESEND message. So every packet requires transmission time and ACK time. This is why latency calculations are multiplied by 2 in all formulas. There are newer protocols that allow multiple packets to be sent and only acknowledging the bundle; but this isn't wildly implemented yet, so it's best to 2x on latency times as a safe way to know what your experience will be.

The average worldwide latency between regions is about 125ms, but can be as little as 8.1ms and a high as 315ms (source: Verizon). This is the transmission times between major data centres. The time from the APP to the data centre or from the data centre to the server also has to be taken into consideration. For example, when I ping my database server, I get 65 to 115ms ... and this is a ping with no data exchange. Therefore the amount of latency you might get can vary greatly depending on traffic and your ISP's overhead.

An APPs data transmission time is calculated as LATENCY * PACKETS * 2. Therefore 20 packets of data exchange is:

  • MINIMUM = 8.1ms * 20 * 2 = 324ms = 0.3 seconds
  • AVERAGE = 125ms * 20 * 2 = 5000ms = 5 seconds
  • MAXIMUM = 315ms * 20 * 2 = 12600ms = 12.6 seconds

When hosting a networked game, you need to build for the average latency but be aware that their may be some players with 315ms latency too.

Data Processing Latency

By far, the most amount of time in the equation is data I/O. It can take a fractions of seconds to a few seconds, to even dozens of seconds to execute a single request against a database. NEODB's database format is not very complicated, so you won't see large data I/O except when requesting large datasets of more than 500 rows. Therefore, you should try to keep the amount of data you return to 100 or fewer rows.

NEODB implements paging, so instead of returning all 100 rows, you might consider seeking 10 rows at a time. Most users don't look at that much data in most cases so why return data they never look at. It's like searching in Google ... after a search, how many google pages do you look through? 1-3 max normally. The same can be said for your app. NEODB offers the ability to search with start and limit parameters.

How fast your data is extracted depends on your ISP. On a server farm like AWS or Azure, you can control how much memory is used by the database to speed up the data operations against your database. This can cost a lot of money, so to minimize costs, you will want a shared host, which means your data times will be higher.

The NEODB database is indexed in a way to increase the speed of data requests, but things can still be slow depending on how your ISP manages it's shared database. On a popular shared host I use for testing, GoDaddy, it takes 1-2 second to execute a request, but the first time can take 6-10 seconds to load the database prepared and ready for queries.

Processing Latency

Once the data is available and has been processed, it can be time consuming to process the results. NEODB's server has very little processing needs. You make a call, it performs the action, then sends back a result. While NEODB's environment has a low overhead, using a PHP server is interpreted which means the server has to load the PHP file, interpret it, then run it. This can be slowish if the ISP doesn't cache the compiled PHP.

Where you will likely see the most processing latency is in your app/game through a poorly crafted game code.

Example Software Architecture in a Networked Environment

In any kind of networked data environment, it's best to minimize the number of actions you make over the network and against your database. So let's consider a top 100 list that is shown with 10 pages of data. We have four ways to implement this. Clearly one way is the best, but I'm using the other 3 to illustrate how poor networking choices can have a dramatic effect on your performance.

For these examples, we'll use the Internet average network latency of 125ms and a packet size of 256 bytes. Different PHP servers use different packet sizes, so times are all based on these packet size settings. Larger packets require fewer round trips but often was transmission space because a full packet must be transmitted even if only a portion of it is used. Further, we'll assume that our request is 80 bytes and is 1 packet. The object data is 200 bytes per row and the indexed data containing the score is 8 bytes and stored as a string; right justified and padded with zeroes so that sorting, can be done on a string, example "000512014". Calculation assumes 100 or more rows of data can be returned.

Options (latency)

  • Object Approach
    • Get all the data first (29.75 seconds / 100 rows)
    • Get the data in pages (5.75 seconds / 10 rows) (5.2x faster)
  • Indexed Data Approach
    • Get indexed data only in pages (1.75 seconds / 10 rows) (17x faster)
    • Get indexed data only all 100 rows (6.125 seconds / 100 rows) (4.8x faster)

Overall, if they look at all ten pages, the cumulative wait time will be more than getting all 100 rows to begin with. However, the architecture is betting that they won't look at every page, perhaps the top 2 or 3 pages. So while the paged solution will be longer for 100 rows overall, we're betting that the average user will only need 3 I/Os and will still be a lot faster overall.

OPTION 1 - Object Approach - Get all the data first.

Find All on Score Index, Sort Descending, LIMIT 100.

Latency Calculation on 100 rows

  • Outbound data will require 80 bytes (1 packet)
    • LATENCY = 1 packet * 2 * 125ms = 250ms
  • Processing and Database Latency = 5000ms
  • Return data will require 100 rows of 200 bytes per row
    • DATA = 100 x 200 = 20000 x 10 bits / 8 bits/byte = 25000 bytes / 256 bytes/packet = 98 packets
    • LATENCY = 98 packets * 2 * 125ms = 24500ms
  • Total Latency = 250ms + 5000ms + 24500ms = 29.75 seconds

The problem with this strategy is that it assumes the user will look at all 100 rows all of the time. Realistically, most only look at the first few pages of a google search, so, it's unlikely that all data is required.

OPTION 2 - Object Approach - Get the data in pages.

Find All on Score Index, Sort Descending, do limited selections; page start for 10 rows.

Latency Calculation on 10 row pages

  • Outbound data will require 80 bytes (1 packet)
    • LATENCY = 1 packet * 2 * 125ms = 250ms
  • Processing and Database Latency = 3000ms (less data to find)
  • Return data will require 10 rows of 200 bytes per row
    • This is done by using the limit option, row 41 for 10 rows is the 4th page
    • DATA = 10 x 200 = 2000 x 10 bits / 8 bits/byte = 2500 bytes / 256 bytes/packet = 10 packets
    • LATENCY = 10 packets * 2 * 125ms = 2500ms
  • Total Latency = 250ms + 3000ms + 2500ms = 5.75 seconds

Instead of waiting 30 seconds for all 100 rows, the user only needs to wait 6 seconds per 10 rows.

OPTION 3 - Indexed Data Approach (both approaches)

Normally when you search with an index, you return the object the index references. The original index only had the score, therefore, you would need to get the object as well to get fill out the columns in a top-score table.

NEODB has another option called an Index find, where you get back only the data from the index. Since EODB supports the creation of index data derived from the data, it is possible to create an index specifically for the top score and and prep the top-score table as scores are saved. If the table contained SCORE, USER, DATE, LEVELS, then creating a string index might look like "000140022;Elenesski;20150107;14" and have an average length of 40.

  • Find All on a special top Index, Sort Descending, LIMIT page start for 10 rows.

Latency Calculation on 10 row pages

  • Outbound data will require 80 bytes (1 packet)
    • LATENCY = 1 packet * 2 * 125ms = 250ms
  • Processing and Database Latency = 500ms (no table join required and a fast pull)
  • Return data will require 10 rows of 40 bytes per row
    • This is done by using the limit option, row 41 for 10 rows is the 4th page
    • DATA = 10 x 40 = 400 x 10 bits / 8 bits/byte = 500 bytes / 256 bytes/packet = 2 packets
    • LATENCY = 2 packets * 2 * 125ms = 1000ms
  • Total Latency = 250ms + 500ms + 1000ms = 1.75 seconds

Clearly this is the best option, because the CPU need to get all of the data needed for a top score is done at the time the index is created. This saves time during the storage of the object means that processing is not required to build the top scores.

Latency Calculation on all 100 rows:

  • Outbound data will require 80 bytes (1 packet)
    • LATENCY = 1 packet * 2 * 125ms = 250ms
  • Processing and Database Latency = 1000ms (no table join required and a fast pull)
  • Return data will require 100 rows of 40 bytes per row
    • This is done by using the limit option, row 41 for 10 rows is the 4th page
    • DATA = 100 x 40 = 4000 x 10 bits / 8 bits/byte = 5000 bytes / 256 bytes/packet = 20 packets
    • LATENCY = 20 packets * 2 * 125ms = 5000ms
  • Total Latency = 250ms + 1000ms + 5000ms = 6.125 seconds