I'm not going to go into details of why sharding is important for App Engine apps except for this quote from Google's article...
I'll get to the implementation of the counter shorty, but first lets have a look at the kind of data it will end up storing in the data store. There are two properties that store data - count and target. The count property is obvious, it stores the shard's counter value, the target property stores the entity that we're counting. The way I approached my implementation, the target is made up of the entity type concatenated with its ID. In reality the target can be any value you want to have a counter for.
As can be seen in the image above, the shards all have the same target. Their individual counter values differ because the total sharded counter value is made up of the sum of all of the shards. Another important point is the key name for the shards is made up of the target and the shard number. This makes it trivial to look up a shard directly and increment its value.
Let start looking at the code. First is the CounterShardEntity class. This class encapsulates the data that is going to be stored in the Data Store. There's not too much logic here. The constructor is used to set up the initial value of the entity - this is used the very first time the shard is created in the Data Store. It expects an array with keys (target, shardNum and count) as input. These are self explanatory.
The kind() function is used to get the Data Store entity kind based on the class name. It also prepends a prefix, KIND_PFX which is set to an empty string but can be customised to any string as desired.
The schema() function defines the data schema for this entity, which is a good practice when working with php-gds. This ensures that the correct properties and their types are set up and also defines the PHP class to be used with this entity.
The next class is CounterShardDS. This is what is going to be used to interact with the counter. This class serves as the Data Store interface to the CounterShardEntity instances. The NUM_SHARDS constant defines how many shards the counter will have. Once in use this constant can be increased but never decreased in value. The constructor doesn't do much apart from instantiating a php-gds DataStore class for the CounterShardEntity and recording the target for later use.
The getCount() function queries all of the counter shards for the same target and adds up their counters to return the total count. It's actually quite straight forward.
The increment() function is a little more complex but not by much. It generates a random shard number and tries to look that shard up. At the same time it gets the overall count. The shard is looked up by key name in the data store - if it's found, great, otherwise a new CounterShardEntity is created with 0 initial count. The shard counter (existing or new) is then incremented and the shard is stored in the Data Store via the upsert() function. The return value is the current count plus 1.
Now that that is out of the way it's time to look at how the counter is used. This code demonstrates looking up the current value of the counter and then incrementing it...
It should be easy to follow. The counter is an instance of the CounterShardDS class. When creating it, the entity type and ID are passed in and that's all that's required. The rest is self explanatory.
So there you go, this is quite a versatile sharded counter implementation for the Google Data Store in PHP. It can be used to count on pretty much anything as long as its type and ID can be specified.