Data Modelling In Google App Engine For Java

8,136 views

Published on

This technical document takes a look at some tips and techniques for data modelling using Google's App Engine datastore and in particular the App Engine for Java

Published in: Technology
1 Comment
8 Likes
Statistics
Notes
No Downloads
Views
Total views
8,136
On SlideShare
0
From Embeds
0
Number of Embeds
206
Actions
Shares
0
Downloads
175
Comments
1
Likes
8
Embeds 0
No embeds

No notes for slide

Data Modelling In Google App Engine For Java

  1. 1. Data Modelling in Google App Engine for Java Guidelines and tips
  2. 2. Who is this presentation for? <ul><li>This presentation is aimed at data modellers and developers interested in our experiences with the Google App Engine for Java (GAEJ) and in particular data modelling. </li></ul><ul><li>We’ve tried to introduce a bit of background for each of the topics, but we do make some assumptions: </li></ul><ul><ul><li>We assume familiarity with Java syntax </li></ul></ul><ul><ul><li>We also assume you’ve done some data modelling before and are familiar with basic relational or object principles </li></ul></ul><ul><ul><li>If not, there are some great resources that you can find via Google that explain the basics of data modelling. </li></ul></ul><ul><li>This presentation outlines some of the things we’ve learned about the datastore whilst developing apps on GAEJ. </li></ul><ul><ul><li>These aren’t immutable laws but merely some guidelines that may make things easier </li></ul></ul><ul><ul><li>We encourage readers to test things out themselves. </li></ul></ul><ul><li>When we put code snippets we use JDO and annotations as that’s our preference </li></ul><ul><ul><li>Rather than JPA and XML </li></ul></ul>Slide
  3. 3. Google App Engine for Java: What and why? <ul><li>Google App Engine (GAE) is a cloud-computing platform that allows users to build and deliver applications over the internet </li></ul><ul><li>Originally, it was released with support only for Python, but in 2008 extended to provide Java support with Google App Engine for Java (GAEJ) </li></ul><ul><li>Google charge depending on usage (storage, bandwidth, CPU) </li></ul><ul><li>It provides a cheap way to deploy scalable web applications in Java </li></ul><ul><li>The development environment built in Eclipse and it is fast and easy to develop applications </li></ul><ul><li>As well as Google’s proprietary datastore there are other built-in services (email, chat, queues, etc.) </li></ul>Slide
  4. 4. Working with the datastore <ul><li>Google allows applications deployed on the GAE to persist data in a data storage repository (“the datastore”) </li></ul><ul><ul><li>It’s the only storage choice available when using the GAE </li></ul></ul><ul><li>It’s an Object database built on Google’s proprietary BigTable implementation </li></ul><ul><li>To comply with existing standards and make things as painless as possible to Java developers Google App Engine for Java (GAEJ) exposes this datastore via JPA and JDO </li></ul><ul><ul><li>In addition there is a low-level API also provided </li></ul></ul><ul><ul><li>Also, there are some non-implemented parts of the JDO spec </li></ul></ul><ul><li>There are big differences in the design and implementation choices that can be made between the datastore and traditional relational databases (RDBs) </li></ul><ul><ul><li>These difference are often obscured by the fact that developers use JDO/JPA which are more commonly associated with ORMs </li></ul></ul>Slide
  5. 5. Reader beware! <ul><li>Some of these guidelines mean that your model will not follow strict data modelling design rules (either relational or object) </li></ul><ul><ul><li>We are both designers and developers and yes we do understand those rules ... </li></ul></ul><ul><ul><li>We also understand that (some) designers can feel passionately about those rules ... </li></ul></ul><ul><ul><li>However we have chosen to make things developer-friendly rather than designer-friendly. </li></ul></ul><ul><ul><li>When designers get called out on support calls at 1 a.m. we will revise these guidelines to be more designer-friendly  </li></ul></ul><ul><li>The tips in the following section (as are the others) are rules-of-thumb </li></ul><ul><li>Sometimes they make extra work in other areas but simplify dealing with the datastore </li></ul><ul><li>As always, design of real-world artefacts is a trade-off between time, cost, and materials </li></ul><ul><ul><li>We like GAEJ because it is cheap and quick to develop on so have made design choices for our apps that help us work around its differences from other platforms </li></ul></ul><ul><li>The tips are GAEJ-specific and we don’t necessarily recommend them for other data stores </li></ul><ul><ul><li>We are working on the Eternal and Universally Applicable Data Storage Guide ... watch this space  </li></ul></ul>Slide
  6. 6. How this presentation is organised <ul><li>Each of the sections contains some guidelines and rules of thumb that we have found useful </li></ul><ul><li>We use the term data modelling to cover object and relational modelling </li></ul><ul><li>The sections run from high level architecture decisions down through design choices to development choices. Sections are: </li></ul><ul><ul><li>Architecture: Comparison to RDBs and when to choose GAEJ </li></ul></ul><ul><ul><li>Design: Tips for defining your persistence model </li></ul></ul><ul><ul><li>Development: Understanding the persistence lifecycle, and miscellaneous tips </li></ul></ul><ul><li>Space is limited to we only include snippets of code that are relevant </li></ul><ul><li>Where we have found them useful, we include links to other sources. We’d encourage you to read and understand them too. </li></ul><ul><li>The documentation around the datastore is getting a lot better and that is always a good starting point </li></ul><ul><li>The datanucleus JDO documentation is good and definitely worth reading. </li></ul>Slide
  7. 7. Architecture: Relational compared to datastore <ul><li>Relational database </li></ul><ul><li>Relational databases are set-based </li></ul><ul><ul><li>Relations are intersections of sets </li></ul></ul><ul><li>Relational databases are “strongly-typed” </li></ul><ul><ul><li>The table defines the shape of the data (i.e. What they data must look like) </li></ul></ul><ul><li>Uses indexes and foreign keys to navigate </li></ul><ul><ul><li>Easier to navigate relationships </li></ul></ul><ul><li>Supports SQL as a means to access the underlying data </li></ul><ul><ul><li>Access to data independent of implementation </li></ul></ul><ul><li>GAE datastore </li></ul><ul><li>The datastore holds objects rather than rows </li></ul><ul><ul><li>Relations via associations </li></ul></ul><ul><li>The datastore defines Entities but these are not strongly typed </li></ul><ul><ul><li>Different versions of an Entity can have different attributes. Up to the app to deal with this. </li></ul></ul><ul><li>Uses object identity to navigate relationships </li></ul><ul><ul><li>Hold object ids as associations </li></ul></ul><ul><li>No support for SQL or other independent access </li></ul><ul><ul><li>Data access is via applications or GAEJ management console </li></ul></ul>Slide
  8. 8. Architecture: When to choose GAEJ? <ul><li>GAEJ may be a good choice if ... </li></ul><ul><li>Your application is read-biased </li></ul><ul><ul><li>Object datastores can be (crudely) viewed in relational terms as “denormalised”. </li></ul></ul><ul><ul><li>Typically denormalised structures are easier to read than write (as you need to update many more objects / rows in a denormalised write) </li></ul></ul><ul><li>Your application is based around a single key entity </li></ul><ul><ul><li>In order to participate in transactions an objects must be in the same EntityGroup. This is more easily achieved if there is a single key object (e.g. A Survey in a survey application; A User in a directory ... Etc) </li></ul></ul><ul><li>Your application doesn’t have many relationships </li></ul><ul><ul><li>Relationships can be modelled through associations but it is less straightforward to navigate these relationships. </li></ul></ul><ul><ul><li>GAEJ’s JDO and JPA implementations do not support the concept of joins </li></ul></ul><ul><li>Your application does not return large datasets </li></ul><ul><ul><li>Google has recently removed the previous limit of 1000 rows in a result set, but GAEJ isn’t the natural choice for batch or ETL type applications as you will probably want something that can support checkpoints </li></ul></ul><ul><li>GAEJ is not a good choice if ... </li></ul><ul><li>Your object model is large or complex </li></ul><ul><ul><li>GAE is a good service, but larger more complex models will fall foul of its limitations at design time (there is no easy way to use CASE tools to model the data) </li></ul></ul><ul><li>You are working with highly-normalised domain </li></ul><ul><ul><li>E.g. Providing access to a master data store. Normalised data is best represented in a relational database </li></ul></ul><ul><li>Your d ata profile is more write-based </li></ul><ul><ul><li>Writes are more expensive in the datastore owing to the nature of the underlying implementation </li></ul></ul><ul><ul><li>Also because the data is denormalised a write may need to span several entities </li></ul></ul><ul><li>You are updating lots of data at once </li></ul><ul><ul><li>This is an extension of the point above re. efficiency. Also, the datastore does not support the concept of savepoints (although the behaviour can be replicated). </li></ul></ul><ul><li>You have transactions spanning multiple objects </li></ul><ul><ul><li>Transactions in the datastore are limited to objects in the same EntityGroup which can lead to complexity in design (more about this later) </li></ul></ul><ul><ul><li>Having said that, think carefully about whether you really need Transactions. Working with transactions (esp. failed ones) is complex from a business rules perspective and (controversial statement coming) most apps can probably do without them </li></ul></ul>Slide
  9. 9. Design: A bit about persistence Slide
  10. 10. Design tips: Selecting persistent entities <ul><li>Before you begin annotating your object model we suggest you divide classes in your model in to managed data and reference data </li></ul><ul><li>Managed data </li></ul><ul><ul><li>Things that your application looks after, often from birth to death. For example, in a survey application this might be the survey, the participant, the questions, etc. </li></ul></ul><ul><ul><li>Sometimes the data can be imported (e.g. Getting a list of users from an LDAP and then adding the users’ qualifications to the data) but it is still managed </li></ul></ul><ul><li>Reference data </li></ul><ul><ul><li>Data that supports your applications. For example, country codes, airport codes, currency codes, etc. </li></ul></ul><ul><li>If the data is managed then you need to mark it as persistence managed </li></ul><ul><li>If the data is reference data then you need to choose. </li></ul><ul><ul><li>If you import the data (e.g. our LDAP) example then it may be better to manage the lifecycle (or import and destruction) yourself. Otherwise you end up multi-mastering the data and having to reconcile the two lists. </li></ul></ul><ul><ul><li>If it is reference data that your end-user controls (e.g. A set of product codes) then you might want to mark it as persistent, but make it standalone (i.e. It doesn’t appear in any relationships and is stored by value in the entity it enhances – i.e. denormalised in relational-speak) </li></ul></ul><ul><ul><li>If it can’t be stored by value then it probably isn’t reference data. </li></ul></ul><ul><li>There will be a third, grey area of data </li></ul><ul><ul><li>These are things like customer addresses, customer loyalty levels, blog post category, etc. </li></ul></ul><ul><ul><li>These are usually data that belong or associated with other data (we cover this in the next section) </li></ul></ul><ul><ul><li>It pays to think how and when this data changes. Is it only when the key entity changes, or can they change independently. </li></ul></ul><ul><ul><li>If they can change independently then maybe they should be persistence managed </li></ul></ul>Slide
  11. 11. Design: Dealing with relationships (1/3) <ul><li>Collections </li></ul><ul><ul><li>Ordered collections are supported, but GAEJ needs to create a separate index representing the position in the collection. </li></ul></ul><ul><ul><li>This means that manipulation of the collection can be potentially inefficient - especially if items are being added in the middle of the collection as reindexing will need to occur. </li></ul></ul><ul><ul><li>A good use for an ordered collection would be a set of question options in a survey (as the options are extremely stable once defined). </li></ul></ul><ul><ul><li>A bad use for order would be a league table in an online game (better store in a different data structure that holds the position and then order in the client). </li></ul></ul><ul><li>Foreign key constraints  </li></ul><ul><ul><li>Don't really exist in the sense of referential integrity. There is the concept of a Key object which can be used to associate a child with a parent (and so feels like an FKEY) but there is no checking by the datastore to see whether it exists. </li></ul></ul><ul><li>Owned (parent-child) relationships </li></ul><ul><ul><li>The two entities belong in the same EntityGroup. </li></ul></ul><ul><ul><li>In order to perform an update within a transaction, the entities must belong to the same EntityGroup (as being in the same entity group means that the data is physically co-located)   </li></ul></ul><ul><ul><li>Do this by setting the parent’s key in the child object (see Google's documentation ) </li></ul></ul><ul><ul><li>However,  there is a limitation with this in that GAEJ can only perform 1-10 writes per second to an entity group. This means that if the object(s) involved will receive more writes per second then the call will block. </li></ul></ul>Slide
  12. 12. Design: Dealing with relationships (2/3) <ul><li>What kind of association is it – Composition or Aggregation? </li></ul><ul><li>Composition </li></ul><ul><ul><li>The relationship created and destroyed along with the owning entity (e.g. A survey may have a set of questions associated with it. If you delete the survey then the questions should be deleted too – assuming there isn’t the concept of a question bank), </li></ul></ul><ul><ul><li>In this case think about modelling the object as an embedded object </li></ul></ul><ul><ul><li>An embedded object has to be Serializable and is extracted/condensed with each storage. You don’t have to worry about navigating associations as the objects are stored by value </li></ul></ul><ul><ul><li>Make an object embedded by annotating the property with @Embedded. This is equivalent to @Persistent(serialized=“true”) We prefer the latter as it is not only clearer but it allows you to specify other attributes (more later) </li></ul></ul><ul><ul><li>If you have 1000s of objects then may serializing them isn’t such a great idea as it is much more of an overhead than looking up. </li></ul></ul><ul><li>Aggregation </li></ul><ul><ul><li>A relationship exists between your entity and the other object but the associated object has some kind of independent existence (e.g. Just because a football team folds it doesn’t mean that all the players have to be destroyed) </li></ul></ul><ul><ul><li>How large is your associated object? Do the associated objects get updated in bulk or usually one at a time? </li></ul></ul><ul><ul><li>It may be worth just storing the objectId of the associated object and then resolving the relationship yourself (using a PersistenceManager.getObjectById() ) </li></ul></ul><ul><ul><li>If the objects can be updated independently of the key entity then you probably need to model them as independent persistent entities. You definitely won’t want to model them as embedded objects in this case. </li></ul></ul>Slide
  13. 13. Design: Dealing with relationships (3/3) <ul><li>Can the attributes of the association be denormalised? </li></ul><ul><ul><li>What data do I really need when modelling the association? </li></ul></ul><ul><ul><ul><li>For an address, is the house number and postcode sufficient? </li></ul></ul></ul><ul><ul><li>What data actually changes in the association? </li></ul></ul><ul><ul><ul><li>In a survey participant is it just the answers and the status? </li></ul></ul></ul><ul><ul><li>If so, perhaps these data can be stored in the entity the other side of the relationship </li></ul></ul><ul><ul><li>Updates become a bit more complex as you need to keep the denormalised attributes in step </li></ul></ul><ul><ul><li>If there is a lot of updating happening then denormalising isn’t the right choice </li></ul></ul><ul><li>What use cases cause the association to be updated? </li></ul><ul><ul><li>This is a general way of asking the same questions as above </li></ul></ul><ul><ul><li>But until you understand when and where your data is created then you will not be able to model it right (unless you get lucky) </li></ul></ul><ul><ul><li>We use sequence diagrams to help us </li></ul></ul>Slide <ul><li>The sequence diagram opposite helps us understand and shows a potential case when not to denormalise </li></ul><ul><ul><li>A user completes a survey that causes her status to be updated. We also need to keep track of the number of completions to understand survey uptake </li></ul></ul><ul><ul><li>The example updates a denormalised count in the Survey </li></ul></ul><ul><ul><li>But do we really want to update the survey every time a participant completes the survey? If there are 15 participants maybe? If there are 15,000 participants probably not </li></ul></ul><ul><ul><li>Making it explicit through sequence diagrams can help </li></ul></ul><ul><ul><li>The diagrams can be done with paper and pencil. The important thing is understanding the object lifecycle </li></ul></ul>
  14. 14. Development : The persistence lifecycle (1/2) <ul><li>There are three main states (and lots of sub-states) in the persistence lifecycle </li></ul><ul><ul><li>See the JDO spec for full details: </li></ul></ul><ul><li>Transient   objects behave just like normal POJOs. </li></ul><ul><ul><li>We don’t really talk about these </li></ul></ul><ul><li>Persistent   objects have their behaviour linked to the underlying transactional datastore. </li></ul><ul><ul><li>This means that JDO tracks changes to the instances and refreshes the instance with the values in the datastore and also stores changes back to the datastore. </li></ul></ul><ul><ul><li>They will be under the stewardship of a (single) PersistenceManager . </li></ul></ul><ul><li>Detached   objects are similar to persistent and maintain a datastore identity. </li></ul><ul><ul><li>However, not all fields will be loaded and any attempt to access an unloaded field is denied. </li></ul></ul><ul><ul><li>Detached objects can have their loaded fields changed and these will be stored to the database (if reattached). Detached objects do not adhere to transactional boundaries. </li></ul></ul><ul><li>A web application will typically perform the following: </li></ul><ul><ul><li>Get an object from the datastore based on a user’s request. For example, a clerk calling up a customer’s record </li></ul></ul><ul><ul><li>Manipulate and change the object via the user interface. For example, a clerk adds a postcode to a customer’s record </li></ul></ul><ul><ul><li>Store the newly updated object back in the datastore </li></ul></ul><ul><ul><li>In order to achieve this, the object must be detached from the PersistenceManager ’s stewardship, so that the web layer can make updates to it </li></ul></ul>Slide
  15. 15. Development : The persistence lifecycle (2/2) <ul><li>Detaching </li></ul><ul><ul><li>You can manually detach </li></ul></ul><ul><ul><ul><li>Call PersistenceManager.detachCopy(object) to get a detach a copy of the object or detachCopyAll(collection) to detach persistent collections </li></ul></ul></ul><ul><ul><ul><li>This can be a bit messy and error-prone (if you forget to detach) </li></ul></ul></ul><ul><ul><li>You can detach following a successful transaction (our preference) </li></ul></ul><ul><ul><ul><li>By setting PersistenceManager.detachAllOnCommit(true) </li></ul></ul></ul><ul><ul><ul><li>This can also be set in the JDO configuration file but we prefer to make it explicit in the code </li></ul></ul></ul><ul><li>Detaching @Embedded properties can be quirky </li></ul><ul><ul><li>If you have an embedded collection then it is not detached even when setting detachAllOnCommit (you get an error when trying to access the collection) </li></ul></ul><ul><ul><li>You can work around this by “touching” the object whilst in the transaction </li></ul></ul><ul><ul><ul><li>This effectively fetches the object so that when the detach is called, the collection is present </li></ul></ul></ul><ul><ul><li>A better way is to change the persistence declaration of the object to keep it as embedded but ensure that the collection is in the same fetch group as its object </li></ul></ul><ul><ul><ul><li>Do this through @Persistent(serialized=“true”, defaultFetchGroup=“true”) </li></ul></ul></ul><ul><ul><ul><li>If you have named fetch groups you will need to change this </li></ul></ul></ul>Slide
  16. 16. Development tips: Working with queries <ul><li>Queries in GAEJ looks similar to queries in other ORMs, but there are differences </li></ul><ul><li>The entity you are querying on has to have an index </li></ul><ul><li>There are restrictions on some operators, e.g. <> </li></ul><ul><li>You can’t query on Blob and Text fields </li></ul><ul><li>Sometimes what appears to be a single query will in fact result in two or more queries </li></ul><ul><ul><li>The differences are subtle and non-trivial so we recommend reading the Google documentation </li></ul></ul><ul><li>If you are going to perform a “join-query” – i.e. filtering on an attribute held in a Collection (e.g. find all customers with postcode beginning “W1”) then it is probably worth denormalising (e.g. holding the postcode as an attribute of the Customer) </li></ul><ul><ul><li>You can always make it read-only to callers of the object attribute through class design </li></ul></ul><ul><li>We try to keep our use of queries as simple as possible and only use them to for list behaviours where we return a collection of objects of the same type </li></ul><ul><ul><li>We know that this isn’t always possible, but it is worth thinking of alternatives to other queries (e.g. splitting in to multiple queries, resolving in the middle layer) just to make life easier </li></ul></ul><ul><ul><li>Again, this will depend on how many objects you have, how often the query will be run, how often the data changes </li></ul></ul><ul><ul><li>These are all things that you need to consider before writing your data access code </li></ul></ul>Slide
  17. 17. Development tips: Miscellaneous tips <ul><li>GAEJ provides access to a memcached cache </li></ul><ul><li>We try to use the cache wherever possible </li></ul><ul><ul><li>The cache built in to GAEJ is easy to use and follows the JSR-107 spec </li></ul></ul><ul><ul><li>There is also a low-level API although we have never had occasion to use it </li></ul></ul><ul><ul><li>The cache is significantly faster than using the datastore </li></ul></ul><ul><ul><li>The Google documentation tells you all you need to know about the API </li></ul></ul><ul><li>More important you must understand your objects’ lifecycle before caching </li></ul><ul><ul><li>When will the object in the cache need to be refreshed? </li></ul></ul><ul><ul><li>When should objects be evicted from the cache? </li></ul></ul><ul><ul><li>This exercise is no different in GAEJ than designing caching in other applications </li></ul></ul><ul><li>Rule of thumb #1: We “always” cache reference data </li></ul><ul><ul><li>There will no doubt be an occasion when we don’t cache some reference data  </li></ul></ul><ul><li>Rule of thumb #2: We cache objects that are frequently accessed </li></ul><ul><ul><li>For example, an online survey that is currently in progress </li></ul></ul>Slide
  18. 18. We’d like to hear from you <ul><li>We have several blog post that delve in to more detail on the GAEJ. Feel free to comment on them and share your experiences </li></ul><ul><ul><li>True North blog </li></ul></ul><ul><li>Did you know that Google have a an AppEngine blog oriented to developers? </li></ul><ul><li>We’re always happy to learn from your experiences with the datastore or with GAEJ: </li></ul><ul><ul><li>Contact us via Twitter </li></ul></ul><ul><ul><li>Or email us </li></ul></ul>Slide

×