Saturday, June 20, 2009

Google App Engine Data Store Api is definitely beta

Was very surprised to see that when querying the data store service for a key and the key does not exist the service actually throws an exception instead of returning null or indicating the absence of value in another way. Definitely need a code review from Josh Bloch. Josh wrote in his Effective Java "Exceptions ... should never be used for ordinary control flow".
In Scala it actually makes things very ugly, instead of doing

ds.get(queryKey) match {
case null => {
logger.info("did not got it: " + queryKey.toString)
...
}
case entity: Entity if (null != entity) => {
logger.info("got it: " + entity.toString)
...
}
}
I must go with something like
try {
val entity = ds.get(queryKey)
logger.info("got it: " + entity.toString)
...
}
catch {
case ex: EntityNotFoundException => {
logger.info("did not got it: " + queryKey.toString)
...
}
}
Which may totally disrupt a nice chain of pattern matching.
Keeping an eye on Issue 1961

5 comments:

Ikai Lan June 21, 2009 at 12:37 PM  

I think this is a very "ActiveRecord" way of doing things, where if you do a Model.find(nonexistent_key) it'll throw a RecordNotFound exception. The reason that paradigm seems to work is specifically because the Rails framework provides some syntatically pleasant ways of handling exceptions for an entire group of methods or for globally handling an exception.

Yeah, I can see why this sucks for GAE. I'm surprised there isn't an option to throw an exception or return null on an invalid key. Next version, I guess.

Daniel June 22, 2009 at 11:43 AM  

def exceptionToRight[T](f : => T): Either[T,java.lang.RuntimeException] =

  try {

    Left(f)

  } catch {

    case ex => Right(ex)

  }



exceptionToRight(ds.get(queryKey)) match {

  case Right(_) => { logger.info("did not got it: " + queryKey.toString)

    ...

  }

  case Left(entity: Entity) if (null != entity) => {
logger.info("got it: " + entity.toString)

    ...

  }

}


Doesn't seem much worse.

Eishay Smith June 22, 2009 at 1:03 PM  

Very nice!
Thanks Daniel.

Paul Kinlan June 22, 2009 at 1:31 PM  

I wonder if it throwing an exception is anything to do with python dictionaries? In python, if you attempt to access a non-existant key in a dictionary it throws an exception.

Jon-Anders Teigen June 22, 2009 at 2:17 PM  

I think the design is correct. get is used for looking up a known key. if the key doesnt exist, that is an exceptional situation. If you dont know if the key exist or not, you should write a query.

Think of it as Map.apply in scala (which also throws an exception for unknown keys), and suddenly its starting to look quite good.

Lacking a proper Option type in java, I very much prefer the explicit EntityNotFoundException over the dreaded NullpointerException.

Creative Commons License This work by Eishay Smith is licensed under a Creative Commons Attribution 3.0 Unported License.