Browse Source

runtime: storage: Add sync working queue.

Shamil Gadelshin 3 years ago
parent
commit
1ef7198437

+ 3 - 1
storage-node-v2/package.json

@@ -56,6 +56,7 @@
     "@types/chai": "^4",
     "@types/mocha": "^5",
     "@types/node": "^10",
+    "@types/pg": "^8.6.1",
     "@types/swagger-ui-express": "^4.1.2",
     "@typescript-eslint/eslint-plugin": "3.8.0",
     "@typescript-eslint/parser": "3.8.0",
@@ -66,6 +67,7 @@
     "globby": "^10",
     "mocha": "^5",
     "nyc": "^14",
+    "pg": "^8.7.1",
     "prettier": "^2.3.0",
     "sinon": "^11.1.1",
     "swagger-ui-express": "^4.1.6",
@@ -122,7 +124,7 @@
     "format": "prettier ./src --write",
     "lint": "eslint ./src --ext .ts",
     "api:edit": "openapi-editor --file ./src/api-spec/openapi.yaml --port 10021",
-    "generate:types:graphql": "yarn graphql-codegen -c ./src/services/query-node/codegen.yml"
+    "generate:types:graphql": "yarn graphql-codegen -c ./src/services/queryNode/codegen.yml"
   },
   "types": "lib/index.d.ts"
 }

+ 54 - 0
storage-node-v2/scripts/generate-test-data.ts

@@ -0,0 +1,54 @@
+#!/usr/bin/env ts-node
+
+import fs from 'fs'
+import path from 'path'
+const fsPromises = fs.promises
+import { Client, ClientConfig } from 'pg'
+import { exit } from 'process'
+
+async function doJob(): Promise<void> {
+  const uploadDirectory = '/Users/shamix/uploads2'
+  const objectNumber = 100
+
+  const config : ClientConfig = {
+    user: 'postgres',
+    password: 'postgres',
+    database: 'query_node_processor'
+  }
+  const client = new Client(config)
+  await client.connect()
+  await client.query('TRUNCATE storage_data_object')
+  
+  const data = new Uint8Array(100000000)
+  const tasks: any[] = []
+  for(let i: number = 1; i <= objectNumber; i++){
+    const name = i.toString()
+
+    console.log(`Writing ${i} data object...`)
+
+    const fileTask = fsPromises.writeFile(
+      path.join(uploadDirectory, name), 
+      data
+      //Buffer.from(name, 'utf8')
+    )
+
+    const dbTask = client.query(
+      `INSERT INTO storage_data_object(storage_bag_id, ipfs_hash, id, created_by_id, version, is_accepted, size) 
+       values('CO', ${name}, ${name}, 'some', '1', false, 100)`
+    )
+
+    tasks.push(dbTask)
+    tasks.push(fileTask)
+  }
+
+  await Promise.all(tasks)
+
+  await client.end()
+}
+
+doJob().then(() => {
+  console.log('Done')
+}).catch((err) => {
+  console.log(err)
+  exit(1)
+})

+ 4 - 3
storage-node-v2/src/services/queryNode/api.ts

@@ -34,7 +34,7 @@ export class QueryNodeApi {
 
   public constructor(endpoint: string) {
     this.apolloClient = new ApolloClient({
-      link: new HttpLink({ uri: endpoint, fetch }),
+      link: new HttpLink({ uri: endpoint, fetch: fetch as any }),
       cache: new InMemoryCache(),
       defaultOptions: {
         query: { fetchPolicy: 'no-cache', errorPolicy: 'all' },
@@ -115,12 +115,13 @@ export class QueryNodeApi {
   }
 
   public getDataObjectDetails(
-    bagIds: string[]
+    bagIds: string[],
+    limit: number
   ): Promise<Array<DataObjectDetailsFragment>> {
     const input: StorageBagWhereInput = { id_in: bagIds }
     return this.multipleEntitiesQuery<
       GetDataObjectDetailsQuery,
       GetDataObjectDetailsQueryVariables
-    >(GetDataObjectDetails, { bagIds: input }, 'storageDataObjects')
+    >(GetDataObjectDetails, { limit, bagIds: input }, 'storageDataObjects')
   }
 }

+ 3 - 3
storage-node-v2/src/services/queryNode/codegen.yml

@@ -5,7 +5,7 @@ overwrite: true
 schema: '../../temp_runtime/joystream/query-node/generated/graphql-server/generated/schema.graphql'
 
 documents:
-  - 'src/services/query-node/queries/*.graphql'
+  - 'src/services/queryNode/queries/*.graphql'
 
 config:
   scalars:
@@ -14,14 +14,14 @@ config:
   skipTypename: true # skip __typename field in typings unless it's part of the query
 
 generates:
-  src/services/query-node/generated/schema.ts:
+  src/services/queryNode/generated/schema.ts:
     hooks:
       afterOneFileWrite:
         - prettier --write
         - eslint --fix
     plugins:
       - typescript
-  src/services/query-node/generated/queries.ts:
+  src/services/queryNode/generated/queries.ts:
     preset: import-types
     presetConfig:
       typesPath: ./schema

+ 3 - 2
storage-node-v2/src/services/queryNode/generated/queries.ts

@@ -43,6 +43,7 @@ export type DataObjectDetailsFragment = {
 
 export type GetDataObjectDetailsQueryVariables = Types.Exact<{
   bagIds?: Types.Maybe<Types.StorageBagWhereInput>
+  limit?: Types.Maybe<Types.Scalars['Int']>
 }>
 
 export type GetDataObjectDetailsQuery = {
@@ -104,8 +105,8 @@ export const GetStorageBagDetails = gql`
   ${StorageBagDetails}
 `
 export const GetDataObjectDetails = gql`
-  query getDataObjectDetails($bagIds: StorageBagWhereInput) {
-    storageDataObjects(where: { storageBag: $bagIds }) {
+  query getDataObjectDetails($bagIds: StorageBagWhereInput, $limit: Int) {
+    storageDataObjects(limit: $limit, where: { storageBag: $bagIds }) {
       ...DataObjectDetails
     }
   }

+ 2 - 2
storage-node-v2/src/services/queryNode/queries/queries.graphql

@@ -43,8 +43,8 @@ fragment DataObjectDetails on StorageDataObject {
   }
 }
 
-query getDataObjectDetails($bagIds: StorageBagWhereInput) {
-  storageDataObjects(where: { storageBag: $bagIds }) {
+query getDataObjectDetails($bagIds: StorageBagWhereInput, $limit: Int) {
+  storageDataObjects(limit: $limit, where: { storageBag: $bagIds }) {
     ...DataObjectDetails
   }
 }

+ 2 - 1
storage-node-v2/src/services/sync/dataObjectsModel.ts

@@ -29,6 +29,7 @@ export async function getRuntimeModel(
 ): Promise<Model> {
   const api = new QueryNodeApi(queryNodeUrl)
   // TODO: graphql response entries limit
+  const limit = 1000
   // TODO: get accepted data objects only
 
   let allBuckets = await api.getAllStorageBucketDetails()
@@ -39,7 +40,7 @@ export async function getRuntimeModel(
   let assignedBags = await api.getStorageBagsDetails(bucketIds)
 
   let bagIds = assignedBags.map((bag) => bag.id)
-  let assignedDataObjects = await api.getDataObjectDetails(bagIds)
+  let assignedDataObjects = await api.getDataObjectDetails(bagIds, limit)
 
   const model: Model = {
     storageBuckets: allBuckets.map((bucket) => ({

+ 150 - 26
storage-node-v2/src/services/sync/synchronizer.ts

@@ -6,18 +6,16 @@ import { pipeline } from 'stream'
 import { promisify } from 'util'
 import fetch from 'node-fetch'
 import urljoin from 'url-join'
+import AwaitLock from 'await-lock'
+import sleep from 'sleep-promise'
 const fsPromises = fs.promises
 
 export async function performSync(): Promise<void> {
   const queryNodeUrl = 'http://localhost:8081/graphql'
   const workerId = 1
+  const processNumber = 3
   const uploadDirectory = '/Users/shamix/uploads'
-
-  // const model = await getRuntimeModel(queryNodeUrl, workerId)
-  // console.log(model)
-
-  // const files = await getLocalFileNames(uploadDirectory)
-  // console.log(files)
+  const operatorUrl = 'http://localhost:3333/'
 
   const [model, files] = await Promise.all([
     getRuntimeModel(queryNodeUrl, workerId),
@@ -34,36 +32,162 @@ export async function performSync(): Promise<void> {
   console.log(`Added: ${added}`)
   console.log(`Deleted: ${deleted}`)
 
-  const operatorUrl = 'http://localhost:3333/'
-
-  await Promise.all(
-    deleted.map((cid) => fsPromises.unlink(path.join(uploadDirectory, cid)))
+  const deletedTasks = deleted.map(
+    (fileName) => new DeleteLocalFileTask(uploadDirectory, fileName)
   )
-
-  await Promise.all(
-    added.map((cid) =>
-      download(
-        urljoin(operatorUrl, 'api/v1/files', cid),
-        path.join(uploadDirectory, cid)
-      )
-    )
+  const addedTasks = added.map(
+    (fileName) => new DownloadFileTask(operatorUrl, fileName, uploadDirectory)
   )
+
+  const workingStack = new WorkingStack()
+  const processSpawner = new TaskProcessorSpawner(workingStack, processNumber)
+
+  workingStack.add(deletedTasks)
+  workingStack.add(addedTasks)
+
+  await processSpawner.process()
+  // const tasks: SyncTask[] = [...deletedTasks, ...addedTasks]
+
+  // await Promise.all(tasks.map((task) => task.execute()))
 }
 
 async function getLocalFileNames(directory: string): Promise<string[]> {
   return fsPromises.readdir(directory)
 }
 
-async function download(url: string, path: string): Promise<void> {
-  console.log('Downloading url:' + url)
+interface SyncTask {
+  description(): string
+  execute(): Promise<void>
+}
+
+class DeleteLocalFileTask implements SyncTask {
+  uploadsDirectory: string
+  filename: string
+
+  constructor(uploadsDirectory: string, filename: string) {
+    this.uploadsDirectory = uploadsDirectory
+    this.filename = filename
+  }
+
+  description(): string {
+    return `Deleting local file: ${this.filename} ....`
+  }
+
+  async execute(): Promise<void> {
+    const fullPath = path.join(this.uploadsDirectory, this.filename)
+    return fsPromises.unlink(fullPath)
+  }
+}
+
+class DownloadFileTask implements SyncTask {
+  filepath: string
+  url: string
+
+  constructor(baseUrl: string, filename: string, uploadsDirectory: string) {
+    this.filepath = path.join(uploadsDirectory, filename)
+    this.url = urljoin(baseUrl, 'api/v1/files', filename)
+  }
+
+  description(): string {
+    return `Downloading file: ${this.url} as ${this.filepath} ....`
+  }
+
+  async execute(): Promise<void> {
+    const streamPipeline = promisify(pipeline)
+
+    const response = await fetch(this.url)
+
+    if (!response.ok)
+      throw new Error(`Unexpected response ${response.statusText}`)
+
+    // TODO: check for errors, both for response and filesystem
+    await streamPipeline(response.body, fs.createWriteStream(this.filepath))
+  }
+}
+
+interface TaskSink {
+  add(tasks: SyncTask[]): Promise<void>
+}
+
+interface TaskSource {
+  get(): Promise<SyncTask | null>
+}
+
+class WorkingStack implements TaskSink, TaskSource {
+  workingStack: SyncTask[]
+  lock: AwaitLock
+
+  constructor() {
+    this.workingStack = []
+    this.lock = new AwaitLock()
+  }
+
+  async get(): Promise<SyncTask | null> {
+    await this.lock.acquireAsync()
+    const task = this.workingStack.pop()
+    this.lock.release()
+
+    if (task !== undefined) {
+      return task
+    } else {
+      return null
+    }
+  }
+
+  async add(tasks: SyncTask[]): Promise<void> {
+    await this.lock.acquireAsync()
+
+    if (tasks !== null) {
+      this.workingStack.push(...tasks)
+    }
+    this.lock.release()
+  }
+}
+
+class TaskProcessorSpawner {
+  processNumber: number
+  taskSource: TaskSource
+  constructor(taskSource: TaskSource, processNumber: number) {
+    this.taskSource = taskSource
+    this.processNumber = processNumber
+  }
 
-  const streamPipeline = promisify(pipeline)
+  async process(): Promise<void> {
+    const processes = []
 
-  const response = await fetch(url)
+    for (let i: number = 0; i < this.processNumber; i++) {
+      const processor = new TaskProcessor(this.taskSource)
+      processes.push(processor.process())
+    }
 
-  if (!response.ok)
-    throw new Error(`Unexpected response ${response.statusText}`)
+    await Promise.all(processes)
+  }
+}
 
-  // TODO: check for errors, both for response and filesystem
-  await streamPipeline(response.body, fs.createWriteStream(path))
+class TaskProcessor {
+  taskSource: TaskSource
+  exitOnCompletion: boolean
+
+  constructor(taskSource: TaskSource, exitOnCompletion: boolean = true) {
+    this.taskSource = taskSource
+    this.exitOnCompletion = exitOnCompletion
+  }
+
+  async process(): Promise<void> {
+    while (true) {
+      console.log('Processing....')
+      const task = await this.taskSource.get()
+
+      if (task !== null) {
+        console.log(task.description())
+        await task.execute()
+      } else {
+        if (this.exitOnCompletion) {
+          return
+        }
+
+        await sleep(3000)
+      }
+    }
+  }
 }

+ 128 - 21
yarn.lock

@@ -1849,6 +1849,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.12.1":
+  version "7.14.8"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
+  integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.9":
   version "7.13.10"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
@@ -4597,18 +4604,6 @@
     is-ipfs "^0.6.0"
     recursive-fs "^1.1.2"
 
-"@polkadot/api-contract@4.2.1":
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-4.2.1.tgz#8fbfd22e5369cceed9afd21bd3cd475b708b3783"
-  integrity sha512-ZHYIEox6pXrAVjZ99Te0kFlU5KcEblnq+dxGfvNisGKv5xieWFOBqJ/azXmm4LKMer/6uzglBv2IRh2QxE+MnA==
-  dependencies:
-    "@babel/runtime" "^7.13.10"
-    "@polkadot/api" "4.2.1"
-    "@polkadot/types" "4.2.1"
-    "@polkadot/util" "^6.0.5"
-    "@polkadot/x-rxjs" "^6.0.5"
-    bn.js "^4.11.9"
-
 "@polkadot/api-derive@4.2.1":
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.2.1.tgz#848a2a9ef947f08660af2571f72ca2b06969f2e3"
@@ -4774,6 +4769,13 @@
   dependencies:
     "@babel/runtime" "^7.13.9"
 
+"@polkadot/networks@^3.7.1":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-3.7.1.tgz#01e568e0f7791c22eb896ffabc23e936ede57c43"
+  integrity sha512-kBPUxt3d1xXeJaFilyVI717TKOZJko/3pvFIDqbSc0i2qdXv8bmRR5r7KMnEB7MvTeMPKHVhcesWksAIdsYRew==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+
 "@polkadot/react-identicon@^0.57.3":
   version "0.57.3"
   resolved "https://registry.yarnpkg.com/@polkadot/react-identicon/-/react-identicon-0.57.3.tgz#f2f1a9b57faa66e1df47a0238daa9607f76d946c"
@@ -4912,7 +4914,7 @@
     "@babel/runtime" "^7.10.5"
     color "^3.1.2"
 
-"@polkadot/util-crypto@6.0.5", "@polkadot/util-crypto@^3.0.1", "@polkadot/util-crypto@^6.0.5":
+"@polkadot/util-crypto@6.0.5", "@polkadot/util-crypto@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-6.0.5.tgz#347ea2bf051d34087766cb43004062358cd43800"
   integrity sha512-NlzmZzJ1vq2bjnQUU0MUocaT9vuIBGTlB/XCrCw94MyYqX19EllkOKLVMgu6o89xhYeP5rmASRQvTx9ZL9EzRw==
@@ -4934,7 +4936,27 @@
     tweetnacl "^1.0.3"
     xxhashjs "^0.2.2"
 
-"@polkadot/util@6.0.5", "@polkadot/util@^3.0.1", "@polkadot/util@^6.0.5":
+"@polkadot/util-crypto@^3.0.1":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-3.7.1.tgz#69e1cca5adc521cf0880b244dc1ae0d086c42e4c"
+  integrity sha512-ZxQa10bo85YlxfS8ieDUzmFZMkKWwOp2dGQ0Xy94e4VBkWVPq9JjAfm8RnLy6D7k5KvMhzKuzJk7IcBDDdXGSw==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+    "@polkadot/networks" "^3.7.1"
+    "@polkadot/util" "^3.7.1"
+    "@polkadot/wasm-crypto" "^1.4.1"
+    base-x "^3.0.8"
+    blakejs "^1.1.0"
+    bn.js "^5.1.3"
+    create-hash "^1.2.0"
+    elliptic "^6.5.3"
+    js-sha3 "^0.8.0"
+    pbkdf2 "^3.1.1"
+    scryptsy "^2.1.0"
+    tweetnacl "^1.0.3"
+    xxhashjs "^0.2.2"
+
+"@polkadot/util@6.0.5", "@polkadot/util@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.0.5.tgz#aa52995d3fe998eed218d26b243832a7a3e2944d"
   integrity sha512-0EnYdGAXx/Y2MLgCKtlfdKVcURV+Twx+M+auljTeMK8226pR7xMblYuVuO5bxhPWBa1W7+iQloEZ0VRQrIoMDw==
@@ -4947,6 +4969,19 @@
     camelcase "^5.3.1"
     ip-regex "^4.3.0"
 
+"@polkadot/util@^3.0.1", "@polkadot/util@^3.7.1":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-3.7.1.tgz#b7585380a6177814f7e28dc2165814864ef2c67b"
+  integrity sha512-nvgzAbT/a213mpUd56YwK/zgbGKcQoMNLTmqcBHn1IP9u5J9XJcb1zPzqmCTg6mqnjrsgzJsWml9OpQftrcB6g==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+    "@polkadot/x-textdecoder" "^3.7.1"
+    "@polkadot/x-textencoder" "^3.7.1"
+    "@types/bn.js" "^4.11.6"
+    bn.js "^5.1.3"
+    camelcase "^5.3.1"
+    ip-regex "^4.2.0"
+
 "@polkadot/vanitygen@^0.18.1":
   version "0.18.1"
   resolved "https://registry.yarnpkg.com/@polkadot/vanitygen/-/vanitygen-0.18.1.tgz#44839473e3cd1490289cef57c05f0466a4e1db80"
@@ -4973,6 +5008,11 @@
   dependencies:
     "@babel/runtime" "^7.13.9"
 
+"@polkadot/wasm-crypto@^1.4.1":
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-1.4.1.tgz#0a053d0c2587da30fb5313cef81f8d9a52029c68"
+  integrity sha512-GPBCh8YvQmA5bobI4rqRkUhrEHkEWU1+lcJVPbZYsa7jiHFaZpzCLrGQfiqW/vtbU1aBS2wmJ0x1nlt33B9QqQ==
+
 "@polkadot/wasm-crypto@^4.0.2":
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.0.2.tgz#9649057adee8383cc86433d107ba526b718c5a3b"
@@ -5025,6 +5065,13 @@
     "@babel/runtime" "^7.13.9"
     "@polkadot/x-global" "6.0.5"
 
+"@polkadot/x-textdecoder@^3.7.1":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-3.7.1.tgz#2d02bd33df0e5d4818b8d96892a5c8290e967573"
+  integrity sha512-GztrO7O880GR7C64PK30J7oLm+88OMxAUVW35njE+9qFUH6MGEKbtaLGUSn0JLCCtSme2f1i7DZ+1Pdbqowtnw==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+
 "@polkadot/x-textencoder@6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.0.5.tgz#fc851259de97a98f3417e51807c1f5ebe265fdf0"
@@ -5033,6 +5080,13 @@
     "@babel/runtime" "^7.13.9"
     "@polkadot/x-global" "6.0.5"
 
+"@polkadot/x-textencoder@^3.7.1":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-3.7.1.tgz#1fe1884821f255565735b1b5dbb17ee61de51fa3"
+  integrity sha512-39jwEu+gok8hFl/UqBr6WDhSeSr4qblriwM++2Vwrw/298hd5uQ7xtJNZKdrbrPCkExPZhrxwVg/mJTHBpwSng==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+
 "@polkadot/x-ws@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-6.0.5.tgz#bafab6004d88d9273478332a3a040bfef3647619"
@@ -6517,6 +6571,15 @@
     "@types/node" "*"
     "@types/pg-types" "*"
 
+"@types/pg@^8.6.1":
+  version "8.6.1"
+  resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.1.tgz#099450b8dc977e8197a44f5229cedef95c8747f9"
+  integrity sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==
+  dependencies:
+    "@types/node" "*"
+    pg-protocol "*"
+    pg-types "^2.2.0"
+
 "@types/prettier@^1.18.3":
   version "1.19.1"
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f"
@@ -9462,11 +9525,21 @@ bluebird@^3.1.1, bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
   integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
 
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0:
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0:
+  version "4.12.0"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+  integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
+
+bn.js@^5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0"
   integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==
 
+bn.js@^5.1.3, bn.js@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
+  integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
+
 body-parser@1.15.2:
   version "1.15.2"
   resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67"
@@ -13396,7 +13469,7 @@ elliptic@^6.5.2:
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.0"
 
-elliptic@^6.5.4:
+elliptic@^6.5.3, elliptic@^6.5.4:
   version "6.5.4"
   resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
   integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
@@ -17816,7 +17889,7 @@ ip-regex@^4.0.0:
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.1.0.tgz#5ad62f685a14edb421abebc2fff8db94df67b455"
   integrity sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==
 
-ip-regex@^4.3.0:
+ip-regex@^4.2.0, ip-regex@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
   integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==
@@ -24832,6 +24905,17 @@ pbkdf2@^3.0.3:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+pbkdf2@^3.1.1:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
+  integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
 peek-readable@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.3.tgz#932480d46cf6aa553c46c68566c4fb69a82cd2b1"
@@ -24926,7 +25010,12 @@ pg-pool@^3.1.1, pg-pool@^3.3.0:
   resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.3.0.tgz#12d5c7f65ea18a6e99ca9811bd18129071e562fc"
   integrity sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==
 
-pg-protocol@^1.2.2, pg-protocol@^1.5.0:
+pg-pool@^3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.4.1.tgz#0e71ce2c67b442a5e862a9c182172c37eda71e9c"
+  integrity sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==
+
+pg-protocol@*, pg-protocol@^1.2.2, pg-protocol@^1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0"
   integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==
@@ -24942,7 +25031,7 @@ pg-types@1.*:
     postgres-date "~1.0.0"
     postgres-interval "^1.1.0"
 
-pg-types@^2.1.0:
+pg-types@^2.1.0, pg-types@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
   integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
@@ -25008,6 +25097,19 @@ pg@^8.3.2:
     pg-types "^2.1.0"
     pgpass "1.x"
 
+pg@^8.7.1:
+  version "8.7.1"
+  resolved "https://registry.yarnpkg.com/pg/-/pg-8.7.1.tgz#9ea9d1ec225980c36f94e181d009ab9f4ce4c471"
+  integrity sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==
+  dependencies:
+    buffer-writer "2.0.0"
+    packet-reader "1.0.0"
+    pg-connection-string "^2.5.0"
+    pg-pool "^3.4.1"
+    pg-protocol "^1.5.0"
+    pg-types "^2.1.0"
+    pgpass "1.x"
+
 pgpass@1.*, pgpass@1.x:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.4.tgz#85eb93a83800b20f8057a2b029bf05abaf94ea9c"
@@ -27931,7 +28033,7 @@ rxjs-compat@^6.6.0:
   resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.6.2.tgz#23592564243cf24641a5d2e2d2acfc8f6b127186"
   integrity sha512-C3V7axnAkPd91sbW1XreL8ydLM+phUcKViM76GBuT3hCzHMSQbszE/h6ajkgcrDn9j4JZ/OdzklvfAJ9MmXRcg==
 
-rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.2, rxjs@^6.6.6:
+rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.6:
   version "6.6.7"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
   integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
@@ -30925,7 +31027,12 @@ typescript-formatter@^7.2.2:
     commandpost "^1.0.0"
     editorconfig "^0.15.0"
 
-typescript@2.2.2, typescript@^3.0.3, typescript@^3.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7:
+typescript@2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c"
+  integrity sha1-YGAiUIR5tV/6NotY/uljoD39eww=
+
+typescript@^3.0.3, typescript@^3.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7:
   version "3.9.7"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
   integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==