diff --git a/package.json b/package.json index ffa27fd..3d2efe4 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@liaoliaots/nestjs-redis": "^10.0.0", + "@nestjs/bullmq": "^11.0.3", "@nestjs/common": "^9.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^9.0.0", @@ -37,6 +38,7 @@ "@nestjs/typeorm": "^11.0.0", "@types/ioredis": "^5.0.0", "axios": "^1.12.2", + "bullmq": "^5.60.0", "crypto": "^1.0.1", "ioredis": "^5.7.0", "isolated-vm": "^6.0.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 03d039f..526b0ec 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,11 +1,11 @@ import { Module } from "@nestjs/common"; import { ApiModule } from "../api/api.module"; -import { QueryModule } from "src/query/query.module"; import { ProjectModule } from "src/project/project.module"; import { RedisModule } from "src/redis/redis.module"; import { DatabaseModule } from "src/database/database.module"; import { DatabaseManagerModule } from "src/databaseManager/database.manager.module"; import { RedisManagerModule } from "src/redisManager/redisManager.module"; +import { QueueModule } from "src/queue/queue.module"; @Module({ imports: [ @@ -14,8 +14,8 @@ import { RedisManagerModule } from "src/redisManager/redisManager.module"; DatabaseManagerModule, ApiModule, ProjectModule, - QueryModule, RedisManagerModule, + QueueModule, ], controllers: [], providers: [], diff --git a/src/query/command/command.controller.ts b/src/query/command/command.controller.ts index 83d76cf..0b1b835 100644 --- a/src/query/command/command.controller.ts +++ b/src/query/command/command.controller.ts @@ -45,7 +45,7 @@ export class CommandController { @Headers() headers: Record, @Res() res: Response ) { - const queryResult = await this.queryExecuterService.runQuery( + const queryResult = await this.queryExecuterService.runQueryQueued( token, query, headers diff --git a/src/query/executer/query.executer.service.ts b/src/query/executer/query.executer.service.ts index 377c1ae..7ce62fd 100644 --- a/src/query/executer/query.executer.service.ts +++ b/src/query/executer/query.executer.service.ts @@ -10,14 +10,22 @@ import { registeredPlugins, } from "src/vm/vm.constants"; import { DatabaseManagerService } from "src/databaseManager/database/database.manager.service"; +import { InjectQueue } from "@nestjs/bullmq"; +import { QUEUE_NAMES } from "src/queue/constants"; +import { Queue, QueueEvents } from "bullmq"; @Injectable() export class QueryExecuterService { + private queueEvents: QueueEvents; + constructor( @InjectRepository(Query) readonly queryRepository: Repository, - readonly databaseManagerService: DatabaseManagerService - ) {} + readonly databaseManagerService: DatabaseManagerService, + @InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue + ) { + this.queueEvents = new QueueEvents(this.queryQueue.name); + } parseImports(source: string): string[] { const importRegex = @@ -38,6 +46,21 @@ export class QueryExecuterService { .trim(); } + async runQueryQueued( + token: string, + queryData: any, + headers: Record = {} + ): Promise { + const job = await this.queryQueue.add(`${new Date().getTime()}_${token}`, { + token, + queryData, + headers, + }); + + const result = await job.waitUntilFinished(this.queueEvents); + return result; + } + async runQuery( token: string, queryData: any, @@ -120,4 +143,8 @@ export class QueryExecuterService { ) ); } + + async onModuleDestroy() { + await this.queueEvents.close(); + } } diff --git a/src/query/handler/query.controller.ts b/src/query/handler/query.controller.ts index c96907f..f9a8e1a 100644 --- a/src/query/handler/query.controller.ts +++ b/src/query/handler/query.controller.ts @@ -45,7 +45,7 @@ export class QueryController { @Headers() headers: Record, @Res() res: Response ) { - const queryResult = await this.queryExecuterService.runQuery( + const queryResult = await this.queryExecuterService.runQueryQueued( token, query, headers diff --git a/src/queue/constants.ts b/src/queue/constants.ts new file mode 100644 index 0000000..ed52abf --- /dev/null +++ b/src/queue/constants.ts @@ -0,0 +1,3 @@ +export const QUEUE_NAMES = { + QUERY: "query-queue", +}; diff --git a/src/queue/processors/query.processor.ts b/src/queue/processors/query.processor.ts new file mode 100644 index 0000000..b4534e0 --- /dev/null +++ b/src/queue/processors/query.processor.ts @@ -0,0 +1,23 @@ +import { Processor, WorkerHost } from "@nestjs/bullmq"; +import { Job } from "bullmq"; +import { QueryExecuterService } from "src/query/executer/query.executer.service"; +import { QUEUE_NAMES } from "../constants"; + +export interface QueryJob { + token: string; + queryData: any; + headers: Record; +} + +@Processor(QUEUE_NAMES.QUERY, { concurrency: 5 }) +export class QueryProcessor extends WorkerHost { + constructor(private readonly queryExecuterService: QueryExecuterService) { + super(); + } + + async process(job: Job) { + const { token, queryData, headers } = job.data; + + return await this.queryExecuterService.runQuery(token, queryData, headers); + } +} diff --git a/src/queue/queue.module.ts b/src/queue/queue.module.ts new file mode 100644 index 0000000..74d5bde --- /dev/null +++ b/src/queue/queue.module.ts @@ -0,0 +1,21 @@ +import { BullModule } from "@nestjs/bullmq"; +import { forwardRef, Module } from "@nestjs/common"; +import { QueryProcessor } from "./processors/query.processor"; +import { QUEUE_NAMES } from "./constants"; +import { QueryModule } from "src/query/query.module"; + +@Module({ + imports: [ + forwardRef(() => QueryModule), + BullModule.forRoot({ + connection: { + host: process.env.REDIS_HOST || "localhost", + port: parseInt(process.env.REDIS_PORT) || 6379, + }, + }), + BullModule.registerQueue({ name: QUEUE_NAMES.QUERY }), + ], + providers: [QueryProcessor], + exports: [BullModule], +}) +export class QueueModule {} diff --git a/yarn.lock b/yarn.lock index f09d1cc..c65ab17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -113,7 +113,7 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== -"@ioredis/commands@^1.3.0": +"@ioredis/commands@1.4.0", "@ioredis/commands@^1.3.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.4.0.tgz#9f657d51cdd5d2fdb8889592aa4a355546151f25" integrity sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ== @@ -184,6 +184,51 @@ resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe" integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA== +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11" + integrity sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz#33677a275204898ad8acbf62734fc4dc0b6a4855" + integrity sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz#19edf7cdc2e7063ee328403c1d895a86dd28f4bb" + integrity sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz#94fb0543ba2e28766c3fc439cabbe0440ae70159" + integrity sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz#4a0609ab5fe44d07c9c60a11e4484d3c38bbd6e3" + integrity sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" + integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== + +"@nestjs/bull-shared@^11.0.3": + version "11.0.3" + resolved "https://registry.yarnpkg.com/@nestjs/bull-shared/-/bull-shared-11.0.3.tgz#b535b8dd42ef007840144d406ca4dbb9b99bfa14" + integrity sha512-CaHniPkLAxis6fAB1DB8WZELQv8VPCLedbj7iP0VQ1pz74i6NSzG9mBg6tOomXq/WW4la4P4OMGEQ48UAJh20A== + dependencies: + tslib "2.8.1" + +"@nestjs/bullmq@^11.0.3": + version "11.0.3" + resolved "https://registry.yarnpkg.com/@nestjs/bullmq/-/bullmq-11.0.3.tgz#c817d84cc4397fb4f920d1cefe44c8c0e1701fbc" + integrity sha512-0Qr7Fk3Ir3V2OBIKJk+ArEM0AesGjKaNZA8QQ4fH3qGogudYADSjaNe910/OAfmX8q+cjCRorvwTLdcShwWEMw== + dependencies: + "@nestjs/bull-shared" "^11.0.3" + tslib "2.8.1" + "@nestjs/cli@^9.0.0": version "9.5.0" resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-9.5.0.tgz#ddf1b0e21b5507c151e0cd1a4cfcf6c55df7cb2e" @@ -1015,6 +1060,19 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +bullmq@^5.60.0: + version "5.60.0" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.60.0.tgz#8c03a88d510a27f965a7cb18e7cdd7510114f6ef" + integrity sha512-hQPfUOLE7/aidCiQSMc5K1EYF6eAuOYSGNdKoLbTpYuRsjYsY0uGUBNX7VrESvAerUpklrsgPLqbunySUxWh5Q== + dependencies: + cron-parser "^4.9.0" + ioredis "^5.4.1" + msgpackr "^1.11.2" + node-abort-controller "^3.1.1" + semver "^7.5.4" + tslib "^2.0.0" + uuid "^11.1.0" + busboy@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -1261,6 +1319,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cron-parser@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" + integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== + dependencies: + luxon "^3.2.1" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1362,6 +1427,11 @@ detect-libc@^2.0.0: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.0.tgz#3ca811f60a7b504b0480e5008adacc660b0b8c4f" integrity sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg== +detect-libc@^2.0.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -2205,6 +2275,21 @@ ioredis@*, ioredis@^5.7.0: redis-parser "^3.0.0" standard-as-callback "^2.1.0" +ioredis@^5.4.1: + version "5.8.0" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.8.0.tgz#a1c4ef6be2e274cc8e99c9e22794ef1ef06dc24a" + integrity sha512-AUXbKn9gvo9hHKvk6LbZJQSKn/qIfkWXrnsyL9Yrf+oeXmla9Nmf6XEumOddyhM8neynpK5oAV6r9r99KBuwzA== + dependencies: + "@ioredis/commands" "1.4.0" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -2469,6 +2554,11 @@ lru.min@^1.0.0: resolved "https://registry.yarnpkg.com/lru.min/-/lru.min-1.1.2.tgz#01ce1d72cc50c7faf8bd1f809ebf05d4331021eb" integrity sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg== +luxon@^3.2.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.2.tgz#d697e48f478553cca187a0f8436aff468e3ba0ba" + integrity sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew== + macos-release@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.1.tgz#bccac4a8f7b93163a8d163b8ebf385b3c5f55bf9" @@ -2627,6 +2717,27 @@ ms@2.1.3, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz#e9d87023de39ce714872f9e9504e3c1996d61012" + integrity sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA== + dependencies: + node-gyp-build-optional-packages "5.2.2" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3" + +msgpackr@^1.11.2: + version "1.11.5" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.5.tgz#edf0b9d9cb7d8ed6897dd0e42cfb865a2f4b602e" + integrity sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA== + optionalDependencies: + msgpackr-extract "^3.0.2" + multer@1.4.4-lts.1: version "1.4.4-lts.1" resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4-lts.1.tgz#24100f701a4611211cfae94ae16ea39bb314e04d" @@ -2709,7 +2820,7 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-abort-controller@^3.0.1: +node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== @@ -2728,6 +2839,13 @@ node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-gyp-build-optional-packages@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz#522f50c2d53134d7f3a76cd7255de4ab6c96a3a4" + integrity sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw== + dependencies: + detect-libc "^2.0.1" + node-releases@^2.0.21: version "2.0.21" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.21.tgz#f59b018bc0048044be2d4c4c04e4c8b18160894c" @@ -3228,7 +3346,7 @@ schema-utils@^4.3.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== @@ -3707,16 +3825,16 @@ tslib@2.7.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== +tslib@2.8.1, tslib@^2.0.0, tslib@^2.1.0, tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0, tslib@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"