BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Introducing FaunaDB Serverless Cloud

Introducing FaunaDB Serverless Cloud

Bookmarks

Key Takeaways

  • The main features of FaunaDB are: serverless, object-relational, graph, globally replicated, ACID, temporal, supports joins, foreign keys, enterprise security, etc..
  • Database queries operate within transactions and can be submitted from serverless Lambda functions or directly by clients at the edge.
  • Object level access control can free developers from the task of setting up an authentication context, and enhance security using defense-in-depth.
  • The article compares using DynamoDB vs. FaunaDB, showing what it takes to port data from the former to the latter.

This spring, Fauna announced the launch of FaunaDB Serverless Cloud, the first database designed expressly to support serverless applications. Unlike cloud database-as-a-service offerings, it provides essential features that enable developers to safely build and run serverless applications without configuring or operating infrastructure.

Fauna started building FaunaDB four years ago, long before the term ‘serverless’ existed. Having experienced the pain of managing servers and database infrastructure, the Fauna team set out to solve those problems. That it's a database perfectly suited to serverless architecture is a matter of convergent evolution. The emergence of serverless as a category of application architecture in 2016 reinforced the validity of Fauna’s approach.

FaunaDB Serverless Cloud is the managed version of FaunaDB, the first adaptive operational database. Fauna DB is an object-relational, globally replicated, strongly consistent, temporal database. It makes all the right tradeoffs. It’s JSON-friendly, in a NoSQL way, but it also supports joins, foreign keys, unique indexes, and other critical correctness features, without the complexity and insecurity of SQL. It also offers native support for distributed graphs and graph functions. It excels at storing highly clustered graphs with billions of nodes and edges, and serving workloads that are impossible in other systems. ACID consistency and enterprise-grade security underpin all of these features, making it useful for a broad range of use cases, from mapping social networks, to incorporating change feeds into apps, to implementing heavy-duty financial applications that need ledgers and global distribution.

Serverlessness

The serverless architecture is becoming increasingly attractive to developers who have struggled to reconcile database infrastructure with modern development practices. While the rest of the world was busy decomposing applications into microservices and embracing the tenets of continuous delivery, databases have remained resolutely monolithic. Other layers of the application stack have become simpler, cheaper, and easier to use. Bucking the trend, database infrastructure is still expensive, brittle, and difficult to operate. As application development becomes more efficient, the pain inflicted by databases becomes more acute.

For many, the concept of a serverless database runs counter to the principles of serverlessness. Serverless architecture is understood as ‘function-as-a-service’ (FaaS), or event-driven architecture. Developers simply code functions and deploy them to the infrastructure. The infrastructure executes those functions whenever they are invoked. The rest of the time the functions are dormant, consuming no resources whatsoever. This sounds like a paradox when applied to serverless databases: serverless functions are ephemeral, yet the data layer is, by definition, persistent.

A big part of the problem is that the databases available today are built for traditional applications, which run continuously from a single geographic location against a fixed set of servers. To use these databases properly, application developers need to know a lot about configuration specifics. In the worst cases, high availability and performance are dependent on developer knowledge of database internals. Developers need figure out and configure things like regions, zones, volumes, memory, software versions, and CPUs. Teams spend time thinking about capacity planning, provisioning, sharding, backups, performance tuning, and monitoring. When you spend 80% of your time setting up and operating databases just to support serverless functions, you know something is amiss. While AWS Lambda and serverless add elasticity to the compute layer, if you need to store data, the persistence layer doesn’t take advantage of that elasticity.

An escape from the provisioning trap

FaunaDB Serverless Cloud addresses these issues with:

  • No provisioning
  • True elastic scale up and scale down (without operator intervention)
  • Pay-as-you-go ‘microbilling’

When building and running apps against FaunaDB Serverless Cloud, developers don’t need to know any infrastructure details, like node size, memory, or storage. In the same way that AWS Lambda charges per single-function invocation, consumption in FaunaDB Serverless Cloud is measured in ‘Fauna Points’. Fauna Points quantify the amount of compute and storage used by different workloads. This is the essence of a serverless database; users are never charged for idle capacity. Database usage reflects function invocation and workload. Period.

With standard DBaaS offerings, over-provisioning is the only strategy available to prepare for traffic spikes. FaunaDB’s pay-as-you-go pricing prevents over-provisioning, which can be wasteful. Since FaunaDB scales elastically without user intervention, developers can launch with no capacity planning. Applications will always have enough capacity, and no idle resources drain the developer’s bank account. The cost of using FaunaDB simply scales with usage. You never pay for unused capacity.

Interacting with a serverless database

FaunaDB hosts an expressive query language highly suited for the serverless environment. There's no need to learn a new syntax, you can encode FaunaDB queries in the language of your choice.

The following is a simple example FaunaDB query that uses Javascript. In this query, we first retrieve a user, and then return a string of the their full name:

var faunadb = require('faunadb'),
  q = faunadb.query;

 

q.Let(
  { user: q.Get(q.Ref(q.Class("users"), "279364341312414")) },
  q.Concat(
    [ q.Select(["data", "first_name"], q.Var("user")),
      q.Select(["data", "last_name"], q.Var("user")) ],
    " "
  )
)

Expressions in other languages are equally simple. Fauna provides drivers for Android, Swift, JavaScript, Ruby, Java, Scala, C#, Python, and Go.

The FaunaDB query language is fully documented here

Most NoSQL query languages offer little more than CRUD operations against data, and it can be difficult to adapt complicated business rules in SQL. But FaunaDB's query language is much closer to a general purpose programming language. Writing your application's logic is intuitive with familiar features such as: if statements, variable assignments, and a rich set of functional programming tools like Map, Foreach, and Filter. And as FaunaDB queries are automatically transactional, you never have to worry about your code crashing in the middle of a query and leaving your data in a half-written state.

Here’s a more powerful example. It returns a change feed of crime watch reports from users in districts in the city of Woodside, excluding the caller's own district. It illustrates graph queries, multilevel joins, indexes, and temporality—all in a single query.

q.Map(
  q.Paginate(
    q.Join(
      q.Intersect(
        q.Join(
          q.Difference(
            q.Match(q.Index("districts_by_city"), "Woodside"),
            q.Ref("classes/districts/1")),
          q.Index("users_by_district")),
        q.Match(q.Index("users_by_group"), "Crime Watch")),
      q.Index("reports_by_user")),
    {
      events: true,
      after: q.Date("2017-1-1")
    }
  ),
  function(event), { return q.Get(q.Select("ref", event)) }
)

A serverless security model

A key distinguishing feature of FaunaDB is its serverless security model. FaunaDB’s intrinsic security means that you don’t need any middleware to perform authentication or authorization checks.This frees developers from writing redundant and potentially error prone boilerplate functions, like code that sets up authentication context or manages app consistency. For example, your serverless FaunaDB database can know who authored a blog post, and then restrict other users to read-only permission.

The following query creates a blog post. Everyone should be able to read the blog post, but you want only the author should be permitted to edit it. We set that up as follows:

q.Create(q.Class("posts"), {

q.Create(q.Class("posts"), {
  data: {
    text: "..."
  },
  permissions: {
    read: "public",
    write: q.Ref(q.Class("users"), 123)
  }
})

Subsequent requests to FaunaDB on behalf of a user can assume their identity, and FaunaDB will enforce the permissions as configured: only the author of the post may edit it and all other users may read it. The lambda can be blissfully unaware of that ownership without opening up a security hole. Since business rules are modelled in the database, your system has additional protection from bugs that might enable unauthorized access to data. It’s easy and secure and removes a lot of the burden from serverless apps. The best part is that, if you’re using an API gateway, the gateway can pass a user-specific database access token to the lambda function. That lets the function transparently interact with FaunaDB as the user.

Serverless Framework

If you’re planning to run functions on a cloud provider like AWS Lambda, you can use the Serverless Framework. The Serverless Framework currently leads the market It provides a clean system for configuring, writing, and deploying serverless application code to different cloud infrastructure providers.

The example below ports one of Serverless’s storage examples from DynamoDB to FaunaDB. This makes it easy to compare a FaunaDB query with one that uses DynamoDB. It also shows how easy it is to set up a serverless environment that uses FaunaDB for persistence.

The app is a simple REST API that enables the creation, updating, and deletion of ‘todo’ items. It can also list all ‘todo’ times. All of the code and instructions can be found here on GitHub.

Serverless Framework provides a format, serverless.yml, that defines a service and links functions to event handlers.

The following is a function definition for readAll:

functions:
  readAll:
    handler: handler.readAll
    events:
      - http:
          path: todos
          method: get
          cors: true

This configuration indicates that the readAll function in handler.js will be called when an HTTP GET is received at the todos path. The functions are linked to handler.js.

module.exports.readAll = (event, context, callback) => {
  todosReadAll(event, (error, result) => {
    const response = {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin" : "*"
      },
      body: JSON.stringify(result),
    };

    context.succeed(response);
  });
};

Everything in handler.js is concerned with managing HTTP, so the actual logic is imported from a module for each function. In this case, todos-read-all.js. This is where FaunaDB comes into play.

'use strict';

const faunadb = require('faunadb');
const q = faunadb.query;
const client = new faunadb.Client({
  secret: process.env.FAUNADB_SECRET
});

module.exports = (event, callback) => {
  return client.query(q.Paginate(q.Match(q.Ref("indexes/all_todos"))))
  .then((response) => {
    callback(false, response);
  }).catch((error) => {
    callback(error)
  })
};

With this setup, you can easily run a query for all todos, using a FaunaDB secret passed via configuration in serverless.yml. This example is simpler than the full object-level access control allows for. To go deeper with access control in FaunaDB, see Chris Anderson’s blog post about authentication and authorization on Serverless.com. FaunaDB uses HTTP for queries so you don’t need to worry about sharing connections between modules or invocations.

The main difference between the DynamoDB and the FaunaDB versions is that this:


return dynamoDb.scan({TableName: 'todos'}, (error, data) => { … })

becomes:

return client.query(q.Paginate(q.Match(q.Ref("indexes/all_todos"))))

Conclusion: What makes FaunaDB different?

FaunaDB Serverless Cloud is the only database specifically built for serverless apps. The setup time is minimal, and there’s no overhead. If you're a business, serverless processes can help you scale your business while actually lowering costs. There’s less setup, operational intervention, and administrative overhead than with any other database. Utility pricing means that your infrastructure costs precisely reflect your resource consumption.

Even though the serverless architecture is new, FaunaDB itself is proven. It was developed against real-world operational workloads and has been running in production with major customers for over a year. It’s actively serving tens of millions of users around the world today. This means you can start building serverless apps with a serverless database with confidence.

Sign up here for a free trial and you’ll get 1,000,000 free Fauna Points.

About the Author

Matt Freels is CTO and cofounder of Fauna. Matt was the Technical Lead of Twitter's database teams. At Twitter, Matt worked with fellow Fauna cofounder Evan Weaver to scale the service through explosive growth. Prior to Twitter, Matt helped scaled Serious Business, a social gaming company that for a time operated at great scale than Twitter. Matt's interests include designing programming languages and building distributed systems. Matt lives in Berkeley, plays bass, and is an avid rock climber.

Rate this Article

Adoption
Style

BT