Charles
Charles

Reputation: 327

What is the scope of the Google App Engine api's namespace manager?

I'm building an API with Flask in Google App Engine Python Standard Environment that is served through multiple domains.

The API can be used to store data and fetch data.

I want to use cloud datastore's multitenancy to store or fetch data only in a namespace determined by the domain from which the API is accessed.

The only way I can see to do this is to use google.appengine.api.namespace_manager to set the namespace either at request time or in a context manager at I/O time.

I wrote this context manager:

@contextmanager
def multitenancy_namespace(namespace):
    original_namespace = namespace_manager.get_namespace()
    if namespace:
        new_namespace = to_namespace_safe_url(namespace)
        namespace_manager.set_namespace(new_namespace)

    yield

    namespace_manager.set_namespace(original_namespace)

And it works as expected.

My worry is the scope of namespace_manager. I have not been able to find any documentation about this.

If my API is threaded and used simultaneously by >1000 users, assuming that the namespace as set by namespace_manager.set_namespace(...) is global, I would expect some collisions - data being stored in the wrong namespace because another request called set_namespace after the first request, but before the first request did its I/O.

I wrote a threaded test here that passes, which tells me that the scope of namespace is at least confined to individual threads (which is good enough for my Flask application).

But what is the context of namespace_manager? What does set_namespace actually do? Where is the namespace setting saved? Is there a use case where a namespace collision could happen?

Upvotes: 2

Views: 319

Answers (1)

Alexander Trakhimenok
Alexander Trakhimenok

Reputation: 6278

If you look to the source code of namespace_manager.set_namespace(...) you'll see it does it by settings the namespace to an environment variable:

def set_namespace(namespace):
  """Set the default namespace for the current HTTP request.
  Args:
    namespace: A string naming the new namespace to use. A value of None
      will unset the default namespace value.
  """
  if namespace is None:
    os.environ.pop(_ENV_CURRENT_NAMESPACE, None)
  else:
    validate_namespace(namespace)
    os.environ[_ENV_CURRENT_NAMESPACE] = namespace

When a thread switches context the AppEngine backups & restores the environment variables as needed. They are guaranteed to be request bounded so it is opaque to user's code. I don't remember if there is docs for this, I think I've learned this at some forum thread.

The comment Set the default namespace for the current HTTP request is implicitly confirms this.

We use it at www.myclasses.org for few years and it's never been a problem.

So relax, you are safe to use it in multi-threaded environment!

Upvotes: 1

Related Questions