Skip to content

Commit

Permalink
Merge pull request #84 from mbret/develop
Browse files Browse the repository at this point in the history
release
  • Loading branch information
mbret authored Mar 7, 2024
2 parents 48c12c0 + 99ef304 commit 6b89141
Show file tree
Hide file tree
Showing 27 changed files with 692 additions and 392 deletions.
14 changes: 3 additions & 11 deletions packages/api/src/functions/refreshMetadata/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { InvokeCommand } from "@aws-sdk/client-lambda"
import { STAGE } from "src/constants"
import { Logger } from "@libs/logger"
import { supabase } from "@libs/supabase/client"
import { isLockOutdated } from "@libs/supabase/isLockOutdated"

const LOCK_MAX_DURATION_MN = 10
const LOCK_MAX_DURATION_MN = 5

const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
event
Expand Down Expand Up @@ -37,18 +38,9 @@ const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
const response = await supabase.from("lock").select().eq("lock_id", lockId)

const lock = (response.data ?? [])[0]
const created_at = new Date(lock.created_at)
const now = new Date()
const differenceInMilliseconds = now.getTime() - created_at.getTime()
const differenceInMinutes = Math.floor(
differenceInMilliseconds / (1000 * 60)
)

Logger.log(
`${lockId} has already a lock created at ${lock.created_at} and is ${differenceInMinutes}mn old`
)

if (differenceInMinutes > LOCK_MAX_DURATION_MN) {
if (isLockOutdated(lock, LOCK_MAX_DURATION_MN)) {
Logger.log(`${lockId} lock is assumed lost and will be recreated`)

const updatedResponse = await supabase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { retrieveMetadataAndSaveCover } from "@libs/books/retrieveMetadataAndSav
import { getParameterValue } from "@libs/ssm"
import { deleteLock } from "@libs/supabase/deleteLock"
import { supabase } from "@libs/supabase/client"
import { Logger } from "@libs/logger"

const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
event
Expand Down Expand Up @@ -120,6 +121,8 @@ const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
deleteLock(supabase, lockId)
])

Logger.log(`lambda executed with success for ${book._id}`)

return {
statusCode: 200,
body: JSON.stringify({})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@ import { handlerPath } from "@libs/handler-resolver"
export default {
handler: `${handlerPath(__dirname)}/handler.main`,
role: "lambdaDefault",
/**
* This lambda can be heavy on local development.
* To avoid memory issues and CPU overuse we lock
* it at 1 concurrency
*/
...(process.env.OFFLINE === "true" && {
reservedConcurrency: 1
}),
timeout: 60 * 15 // 15mn
}
47 changes: 45 additions & 2 deletions packages/api/src/functions/syncDataSource/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { getNormalizedHeader } from "@libs/utils"
import { STAGE } from "../../constants"
import schema from "./schema"
import { InvokeCommand } from "@aws-sdk/client-lambda"
import { Logger } from "@libs/logger"
import { supabase } from "@libs/supabase/client"
import { isLockOutdated } from "@libs/supabase/isLockOutdated"

const LOCK_MAX_DURATION_MN = 10

const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
event
) => {
const lambda = getAwsLambda()
const client = getAwsLambda()

const command = new InvokeCommand({
InvocationType: "Event",
FunctionName: `oboku-api-${STAGE}-syncDataSourceLongProcess`,
Expand All @@ -21,7 +27,44 @@ const lambda: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
})
})

await lambda.send(command)
const lockId = `sync_${event.body.bookId}`

const response = await supabase
.from("lock")
.insert({ id: 1, lock_id: lockId })
.select()

if (response.status === 409) {
const response = await supabase.from("lock").select().eq("lock_id", lockId)

const lock = (response.data ?? [])[0]
const now = new Date()

if (isLockOutdated(lock, LOCK_MAX_DURATION_MN)) {
Logger.log(`${lockId} lock is outdated and will be recreated`)

const updatedResponse = await supabase
.from("lock")
.upsert({ id: lock.id, created_at: now, lock_id: lockId })
.select()

if (updatedResponse.status === 200) {
Logger.log(`${lockId} lock correctly updated, command will be sent`)

await client.send(command)
}
} else {
Logger.log(`${lockId} invocation is ignored`)
}
}

if (response.status === 201) {
Logger.log(
`New lock created for ${lockId} with id ${(response.data ?? [])[0].id}. Command will be sent`
)

await client.send(command)
}

return {
statusCode: 202,
Expand Down
8 changes: 8 additions & 0 deletions packages/api/src/functions/syncDataSource/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import { handlerPath } from "@libs/handler-resolver"
export default {
handler: `${handlerPath(__dirname)}/handler.main`,
role: "lambdaDefault",
/**
* Because this lambda check and lock the process
* we need to avoid concurrent access. This way we ensure
* the lock is always checked in sync.
*
* This lambda should stay simple and fast (check/lock)
*/
reservedConcurrency: 1,
events: [
{
http: {
Expand Down
8 changes: 6 additions & 2 deletions packages/api/src/libs/metadata/google/getGoogleMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { refineTitle } from "../refineTitle"
export const getGoogleMetadata = async (
metadata: Metadata,
apiKey: string
): Promise<Metadata> => {
): Promise<Metadata | undefined> => {
let response = metadata.isbn
? await findByISBN(metadata.isbn, apiKey)
: await findByTitle(metadata.title ?? "", apiKey)
Expand Down Expand Up @@ -42,8 +42,12 @@ export const getGoogleMetadata = async (
}
}

const parsedMetadata = parseGoogleMetadata(response)

if (!Object.keys(parsedMetadata)) return undefined

return {
...parseGoogleMetadata(response),
...parsedMetadata,
type: "googleBookApi"
}
}
3 changes: 3 additions & 0 deletions packages/api/src/libs/supabase/deleteLock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Logger } from "@libs/logger"
import { SupabaseClient } from "@supabase/supabase-js"

export const deleteLock = async (supabase: SupabaseClient, lockId: string) => {
Logger.log(`releasing lock ${lockId}`)

return await supabase.from("lock").delete().eq("lock_id", lockId)
}
10 changes: 10 additions & 0 deletions packages/api/src/libs/supabase/isLockOutdated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Lock } from "./types"

export const isLockOutdated = (lock: Lock, maximumTime: number) => {
const created_at = new Date(lock.created_at)
const now = new Date()
const differenceInMilliseconds = now.getTime() - created_at.getTime()
const differenceInMinutes = Math.floor(differenceInMilliseconds / (1000 * 60))

return differenceInMinutes > maximumTime
}
5 changes: 5 additions & 0 deletions packages/api/src/libs/supabase/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Lock = {
id: string
lock_id: string
created_at: string
}
Loading

0 comments on commit 6b89141

Please sign in to comment.