Reputation: 327
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
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