Skip to content

Instantly share code, notes, and snippets.

@fauna-brecht
Last active November 28, 2022 12:13
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fauna-brecht/c048f7d721061c4e475278a4eb896fd9 to your computer and use it in GitHub Desktop.
Save fauna-brecht/c048f7d721061c4e475278a4eb896fd9 to your computer and use it in GitHub Desktop.
Rate limiting FaunaDb
import { rateLimiting } from '../../fauna-queries/helpers/errors'
import faunadb from 'faunadb'
/*
* Ideally we limit the amount of calls that come to Login.
*/
const q = faunadb.query
const {
If,
Epoch,
Match,
Index,
Collection,
Let,
Var,
Paginate,
Select,
TimeDiff,
Or,
GTE,
Abort,
Create,
IsEmpty,
Count,
LT,
Do,
Now,
Subtract
} = q
function AddRateLimiting(action, FqlQueryToExecute, Identifier, calls, perMilliseconds) {
const ExecuteAndCreateLog = Do(
Create(Collection('logs'), {
data: {
action: action,
identity: Identifier
}
}),
FqlQueryToExecute
)
return Let(
{
logsPage: Paginate(Match(Index('logs_by_action_and_identity_ordered_by_ts'), action, Identifier), {
size: calls
})
},
If(
Or(IsEmpty(Var('logsPage')), LT(Count(Select(['data'], Var('logsPage'))), calls)),
// If no logs exist yet, create one.
ExecuteAndCreateLog,
Let(
{
// the page looks like { data: [timestamp1, timestamp2,...]},
// we will retrieve the last timestamp of that page. If the pagesize would be 3, it would be the oldest of these 3 events.
// since the index is ordered from new to old.
timestamp: Select(['data', Subtract(calls, 1)], Var('logsPage')),
// transform the Fauna timestamp to a Time object
time: Epoch(Var('timestamp'), 'microseconds'),
// How long ago was that event in ms
ageInMs: TimeDiff(Var('time'), Now(), 'milliseconds')
},
If(
GTE(Var('ageInMs'), perMilliseconds),
// Then great we execute
ExecuteAndCreateLog,
// Else.. Abort! Rate-limiting in action
Abort(rateLimiting)
)
)
)
)
}
export { AddRateLimiting }
@fauna-brecht
Copy link
Author

fauna-brecht commented Mar 23, 2020

Creation of collection and indexes

import { DeleteIfExists, IfNotExists } from '../helpers/fql'

const faunadb = require('faunadb')
const q = faunadb.query
const { CreateCollection, CreateIndex, Collection, Index } = q

/* Collection */

// We will only keep the last 10 days of access logs.
const CreateLogsCollection = CreateCollection({ name: 'logs', ttl_days: 10 })

const CreateIndexLogsByAction = CreateIndex({
  name: 'logs_by_action',
  source: Collection('logs'),
  terms: [
    {
      field: ['data', 'action']
    }
  ]
})

const CreateIndexLogsByActionAndIdentity = CreateIndex({
  name: 'logs_by_action_and_identity',
  source: Collection('logs'),
  terms: [
    {
      field: ['data', 'action']
    },
    {
      field: ['data', 'identity']
    }
  ]
})

const CreateIndexLogsByActionAndIdentityOrderedByTime = CreateIndex({
  name: 'logs_by_action_and_identity_ordered_by_ts',
  source: Collection('logs'),
  terms: [
    {
      field: ['data', 'action']
    },
    {
      field: ['data', 'identity']
    }
  ],
  values: [
    {
      field: ['ts'],
      reverse: true
    }
  ]
})

async function createLogsCollection(client) {
  const logsRes = await client.query(IfNotExists(Collection('logs'), CreateLogsCollection))
  await client.query(IfNotExists(Index('logs_by_action'), CreateIndexLogsByAction))
  await client.query(IfNotExists(Index('logs_by_action_and_identity'), CreateIndexLogsByActionAndIdentity))
  await client.query(
    IfNotExists(Index('logs_by_action_and_identity_ordered_by_ts'), CreateIndexLogsByActionAndIdentityOrderedByTime)
  )
  return logsRes
}

async function deleteLogsCollection(client) {
  await client.query(DeleteIfExists(Collection('logs')))
  await client.query(DeleteIfExists(Index('logs_by_action')))
  await client.query(DeleteIfExists(Index('logs_by_action_and_identity')))
  await client.query(DeleteIfExists(Index('logs_by_action_and_identity_ordered_by_ts')))
}

export { createLogsCollection, deleteLogsCollection }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment