Multi-tenancy
Many database systems provide multi-tenant capabilities. They can contain multiple databases, each with their own access controls. FaunaDB takes this much further by allowing any database to have multiple child databases. This enables an operator to manage a single large FaunaDB cluster, create a few top-level databases, and give full administrative access of those databases to the respective teams. Each team is free to create as many databases as they need without requiring operator intervention. As far as the team is concerned, they have their own full FaunaDB cluster.
This tutorial demonstrates how to create multiple databases, create child databases, and provide access secrets.
This tutorial assumes that you have completed the Fauna Shell quick start. |
-
Start Fauna Shell
In a terminal, start Fauna Shell by running:
fauna shell my_db Starting shell for database my_db Connected to https://db.fauna.com Type Ctrl+D or .exit to exit the shell
The secret used to setup and access the database
my_db
is an admin secret, which has permission to create and manage child databases. -
Create the top-level databases
First, create a set of top-level databases that can be handed over to individual teams. Run the following query to create two databases, named
production
andinternal
:Map( ["production", "internal"], Lambda("name", CreateDatabase({ name: Var("name")})) )
You should see output similar to:
[ { ref: Database("production"), ts: 1558574366150000, name: 'production' }, { ref: Database("internal"), ts: 1558574366150000, name: 'internal' } ]
-
Create admin secrets for each database
Here we create admin secrets for each database. These secrets can be handed to each team, and they give the teams full control over their respective databases (but not each other’s).
Map( [Database("production"), Database("internal")], Lambda("db", CreateKey({ role: "admin", database: Var("db") })) )
You should see output similar to:
[ { ref: Ref(Keys(), "233115268679729664"), ts: 1558574894580000, role: 'admin', database: Database("production"), secret: 'fnADPDEaDWACAONo-I_v8FH9DEuWDZKCGQyborRY', hashed_secret: '$2a$05$ufSuq8vPlxU2KpX3qIxTMue59uz51D.VpXwfiZovyMAm.1lcY/IPK' }, { ref: Ref(Keys(), "233115268694409728"), ts: 1558574894580000, role: 'admin', database: Database("internal"), secret: 'fnADQDExDkACAJxK4j3CwxjTWxB2CnqTHowktW4M', hashed_secret: '$2a$05$3XncjrjIPhkPAXX9DsV8B.46FVJZOmSAOF6dsqMz3/p5HiLt1aPDm' } ]
Be sure to save a copy of each secret. Secrets are only displayed when they are created. If you lose a secret, you would need to delete the associated key and make a new one. -
Create per-team child databases
Given the new keys, each team can create their own child databases that fit their needs. In this case, the production team does not need any child databases, but the internal team does.
Open a new terminal window, and start Fauna Shell with the secret for the
internal
database (replace the secret below with the secret generated in the previous command):fauna shell --secret=fnADQDExDkACAJxK4j3CwxjTWxB2CnqTHowktW4M Starting shell for database my_db Connected to https://db.fauna.com Type Ctrl+D or .exit to exit the shell
FaunaDB knows which database to connect to when you provide a secret.
The internal team needs two child databases,
personnel
andbulletin-board
. Run the following query:Map( ["personnel", "bulletin-board"], Lambda("db", CreateDatabase({ name: Var("db") })) )
You should see output similar to:
[ { ref: Database("personnel"), ts: 1558575584530000, name: 'personnel' }, { ref: Database("bulletin-board"), ts: 1558575584530000, name: 'bulletin-board' } ]
-
Create server secrets for the internal team’s child databases
The internal team has an application for each database, so we now need to create server secrets to permit the applications to connect to their respective databases. Server secrets do not have permission to create or manage child databases; they can only be used to connect to their associated database.
Run the following query:
Map( [Database("personnel"), Database("bulletin-board")], Lambda("db", CreateKey({ role: "admin", database: Var("db") })) )
You should see output similar to:
[ { ref: Ref(Keys(), "233116225856602624"), ts: 1558575807350000, role: 'admin', database: Database("personnel"), secret: 'fnADPDH46ZACAIfnmFk883bVBkFVFodHhtVXXtBK', hashed_secret: '$2a$05$WWJkl8bydmoAo8D0KNN/w.4sglkxfDwKtSIYr3RU5jg36uT3l3atm' }, { ref: Ref(Keys(), "233116225856603648"), ts: 1558575807350000, role: 'admin', database: Database("bulletin-board"), secret: 'fnADPDH46ZAGAMjVDSoDHU89i0qsMq9uMArnLzoK', hashed_secret: '$2a$05$ZgftjR/OiajqV3qaDSDl0uHolpaO.HRfHQoM4NaWlinF18bgcIUli' } ]
These secrets should be copied into the respective application’s configuration now; you won’t see the secrets again.
-
Verify the configuration
Now that the databases have been created, which databases can be seen?
In the terminal with Fauna Shell connected to the internal database, run the following query:
Paginate(Databases())
You should see output similar to:
{ data: [ Database("personnel"), Database("bulletin-board") ] }
In the terminal with the Fauna Shell connected to the
my_db
database, run the following query:Paginate(Databases())
You should see output similar to:
{ data: [ Database("production"), Database("internal") ] }
Surprised? FaunaDB only lists the immediate child databases within the connected database. However, the operator can use her admin secret to connect to the child databases to investigate further.
Conclusion
In this tutorial, we demonstrated how to set up a hierarchy of databases, starting with two top-level, broadly-scoped databases and continuing down to individual databases for the internal team. And we saw how to create access secrets and learned how much access those secrets provide.
Fauna Cloud works identically. An organization can build their database hierarchy according to their structure without the overhead of operating their own FaunaDB cluster.
Was this article helpful?
We're sorry to hear that.
Tell us how we can improve!
documentation@fauna.com
Thank you for your feedback!