mallyvai
mallyvai

Reputation: 1738

Odd App Engine NDB behavior w.r.t. get_or_insert and transactions

I have what boils down to these helper methods on an ndb.Model class:

@classmethod
@ndb.transactional(retries=2)
def new(cls, *args, **kwargs):
  internal_name = kwargs['internal_name']
  new_company = cls.get_or_insert(internal_name, **kwargs)
  new_company.put()
  return new_company

@classmethod
@ndb.transactional(retries=2)
def get(cls, internal_name):
  result = cls.query(cls.internal_name == internal_name)
  if result.count() != 1:
    raise Exception("this is a problem");  
  return result.get()

I wrote a test to verify my assumptions:

def testCanCreateRetrieveCompany(self):
  company = self.models.Company.new(internal_name="google", description="search engine")
  reread_company = self.models.RichCompany.get("internal_name")
  self.assertTrue(True)

However, this fails with the this is a problem exception every time!

However, if I change the test to:

def testCanCreateRetrieveCompany(self):
  company = self.models.Company.new(internal_name="google", description="search engine")
  company.put()
  reread_company = self.models.RichCompany.get("internal_name")
  self.assertTrue(True)

this seems to pass. I have absolutely no idea why this passes when the first one doesn't. Also, removing the @ndb.transactional operator seems to make this work. Except for I obviously want this to be transactional.

Not sure what's going on. I know there are caveats in the docs around reads in transactions (only ever see original read-state of transaction) but I wasn't aware there were any such caveats for writes. Does anyone have any insight?

Upvotes: 0

Views: 97

Answers (1)

mallyvai
mallyvai

Reputation: 1738

So after a bit more digging, I think I'm just seeing that the darn updates might be transactional, but are still, in fact eventually consistent - that is, just because the method is transactional, doesn't mean the update made is going to be strongly consistent. I ended up adding a parent/ancestor component to the company keys (so all company objects share a single ancestor) and that gives me the effect I want. See:

ndb and consistency: Why is happening this behavior in a query without a parent

Upvotes: 1

Related Questions