ã¤ãå æ¥ãªãã¥ã¼ã¢ã«ããã ã¹ã¿ãã£ãµã㪠ä¸å¦è¬åº§ ã®éçºãã¼ã ã§ã¤ã³ã¿ã¼ã³çããã¦ãã @YutaUra ã§ããä»åã¯ã¤ã³ã¿ã¼ã³ã®ä¸ã§ New Relic 㨠Prisma ãé£æºãããæ¥åã«åãçµãã ã®ã§ããã«é¢ãã¦ãç´¹ä»ãããã¨æãã¾ãï¼
ã¹ã¿ãã£ãµããªä¸å¦è¬åº§ã§ã¯ãããã¯ã¨ã³ãã®ä¸é¨ã« TypeScript x GraphQL x Prisma ãæ¡ç¨ãã¦ãããããã©ã¼ãã³ã¹ã®è¨æ¸¬ãªã©ã« New Relic ãç¨ãã¦ãã¾ããNew Relic 㯠Prisma ãæ£å¼ã«ãµãã¼ããã¦ããªããããå°å ¥ããããã«ã¯èªåãã¡ã§å®è£ ããå¿ è¦ãããã¾ããã ããã§ä»åã¯ã©ã¤ãã©ãªãä½æããæçµçã« OSSï¼newrelic-node-prismaï¼ã¨ãã¦å ¬éããã®ã§ããããã«ã¤ãã¦å®è£ ã¨åããã¦ç´¹ä»ãã¾ãã
New Relic ã¨ã¯
New Relic ã®ããããã¼ã¸
Web ãµã¼ãã¹ã«ããããã¾ãã¾ãªããã©ã¼ãã³ã¹ãè¨æ¸¬ã»ç£è¦ã»åæããããã®ãã¼ã«ã§ãããªã¯ã¨ã¹ããã¨ã«ããã«ããã¯ã¨ãªã£ã¦ããç®æãç¹å®ããããããã¨ãã§ãã¾ãã
èæ¯ã«ã¤ãã¦
New Relic ã¯æ¨æºã§ Express ã®ãã㪠Web ãã¬ã¼ã ã¯ã¼ã¯å ã®å¦çãã PostgreSQL, MySQL ãªã©ã®ãã¼ã¿ãã¼ã¹ã®ã¯ã¨ãªçã®ããã©ã¼ãã³ã¹ã®è¨æ¸¬ãå¯è½ã¨ãªã£ã¦ãããè¨å®ãã¡ã¤ã«ãä½æãããã¨ã§èªåçã«ãããã®è¨æ¸¬ãè¡ããã¨ãã§ãã¾ãã
ãããã Prisma ã«ã¤ãã¦ã¯æ¨æºã§ã®ãµãã¼ããããã¦ããªãï¼newrelic/node-newrelic#991ï¼ãè¨æ¸¬ãè¡ãããå ´åã¯åèªã§ New Relic 㨠Prisma ãé£æºããããã®è¨è¿°ãããå¿ è¦ãããã¾ãã
ä»åã®ããã¸ã§ã¯ãã§ã¯ãGraphQL ãæ¡ç¨ãã¦ãããã N+1 ãçºçãã¦ããªããã¨ããã®ãç¶ç¶çã«ç£è¦ãã¦ããããã®ä»çµã¿ãå¿ è¦ã§ããã®ããã«ã¯ New Relic 㧠Prisma ãè¨æ¸¬ãããã¨ãå¿ è¦ã§ããã
ãã©ã°ã¤ã³ã®ä½æã«ã¤ãã¦
åé ã§ç´¹ä»ããããã«ãä»åã¯ã©ã¤ãã©ãªãä½æããOSSï¼newrelic/node-newrelic#991ï¼ã¨ãã¦å ¬éãã¾ãããããã§ã¯ä½ãä¸ã§ã®ãã¤ã³ããªã©ç´¹ä»ãã¾ãã
New Relic ã® Prisma ãã©ã°ã¤ã³ãä½æããä¸ã§å¿ è¦ã¨ãªãç¥èã¯ä»¥ä¸ã®éãã§ãã
- New Relic ã® Instrumentã»Segment ã«ã¤ãã¦
- Prisma ã§ã®ã¯ã¨ãªãåå¾ããæ¹æ³ã«ã¤ãã¦
Instrumentã»Segment ã«ã¤ãã¦
New Relic ã§ã¯ããã©ã¼ãã³ã¹ãè¨æ¸¬ãã対象ã Instrument ã¨å¼ãã§ãã¾ããInstrument ã«ã¯ããã¤ã種é¡ããããDatastore, Message Broker, Web Framework ãªã©ãããã¾ãã ããã¦ããããã® Instrument 㯠Segment ã¨ããåä½ã§ãã¾ãã¾ãªè¨æ¸¬ãè¡ãã¾ãã
ä¸è¨ã®ç»åã®ä¾ã§ã¯ prisma-sample ã¨ãã Instrument 㨠Prisma ã¨ãã Instrument ãããã è¤æ°ã® Segment ãè¨æ¸¬ã§ãã¦ãããã¨ããããã¾ãã
ã¾ãã Datastore ã¨ãã¦ç»é²ãããã¨ããã¼ã¿ãã¼ã¹é¢é£ã®ããã«ããã¯ãç¹å®ããããã®ãã¾ãã¾ãªè¨æ¸¬ã»å¯è¦åãè¡ãããããã«ãªãã¾ãã
Prisma ã§ã¯ã¨ãªãåå¾ããæ¹æ³
Prisma 㯠Rust ã§å®è£ ãããç¬èªã¨ã³ã¸ã³ãå é¨ã«æã£ã¦ãã¦ãããã«å¯¾ã㦠GraphQL ã®ã¯ã¨ãªãæãããã¨ã§ãSQL ã¸ã¨å¤æãããã¼ã¿ãåå¾ã»æ´æ°ãããã¨ãã§ããããã«ãªã£ã¦ãã¾ãã Prisma ã§ãã¼ã¿ãã¼ã¹ã¸ã®ãªã¯ã¨ã¹ããåå¾ããå ´åã«ã¯ 2 ã¤ã®æ¹æ³ãããã1 ã¤ç®ã¯ GraphQL ã¨ãã¦è§£éãããç´åã®æ å ±ã使ãæ¹æ³ã§ã2 ã¤ç®ã¯å®è¡ãããå¾ã® SQL ãã¤ãã³ãã¨ãã¦åãåãæ¹æ³ã§ãã
大éæã«å®è£ ããã¨ã以ä¸ã®ããã«ãªãã¾ãã
// æ¹æ³ 1 const prisma = new PrismaClient(); // _executeRequest ã¯å é¨ã¡ã½ããã®ãããå°æ¥çã«å¤æ´ãããå¯è½æ§ãããã¾ãã const _executeRequest = PrismaClient.prototype._executeRequest; prisma.prototype._executeRequest = async (params) => { console.log("START", new Date(), params); const result = await _executeRequest.call(prisma, params); console.log("END", new Date(), params); return result; };
// æ¹æ³ 2 const prisma = new PrismaClient({ log: [{ level: "query", emit: "event" }], }); prisma.$on("query", (event) => { console.log("SQL", event.query); console.log("å®è¡æé", event.duration); });
æ¹æ³ 1 ã¯å é¨ã¡ã½ããã«ä¾åãã¦ããæ¹æ³ã¨ãªã£ã¦ãããã¢ãã¦ã³ã¹ãªãã«å©ç¨ã§ããªããªãå¯è½æ§ãããã¾ããä¸æ¹ã§ãæ¹æ³ 2 㯠Prisma 㧠Event-based logging ããéã®æ¹æ³ãªã®ã§ãããã«å©ç¨ã§ããªããªãã¨ãããã¨ã¯èãã«ããã§ãããããã New Relic ã§ã¯å®éã«ã¯ã¨ãªãããåå¾ã« Segment ã«é¢ããå¦çãè¡ãå¿ è¦ãããããã2 ã¤ç®ã®æ¹æ³ã§ã¯ãªãã1 ã¤ç®ã®æ¹æ³ã使ããã¨ã§ Prisma ã®è¨æ¸¬ãè¡ããã¨ãã§ããããã«ãªãã¾ãã
New Relic agent 㨠Prisma ãé£æºããã
詳細ã«ã¤ãã¦ã¯ å ¬å¼ã®ããã¥ã¡ã³ã ãä¸åº¦èªã¾ãããã¨ããªã¹ã¹ã¡ãã¾ãã
Node.js ã® New Relic agent ã«ã¯ Datastore ãè¨æ¸¬ããããã®ãã«ãã¼é¢æ°ãå¤æ°ç¨æããã¦ãã¦ããããã使ããã¨ã§ç°¡åã«è¨æ¸¬ãããããã®ã³ã¼ããæ¸ããã¨ãã§ãã¾ãã
å ·ä½çãªå®è£ ã交ããªããã New Relic 㨠Prisma ãé£æºããæ¹æ³ãç´¹ä»ãã¾ãã
ã¾ãã New Relic agent ã§ã¯å ç¨ã®æ¹æ³ 1 ã®ããã«ã¢ã¸ã¥ã¼ã«ã® prototype ãæ¸ãæãããã¨ã§è¨æ¸¬ãè¡ãããã«ãã¦ãã¦ããã®ããã¢ããªã±ã¼ã·ã§ã³ã§ãã®ã¢ã¸ã¥ã¼ã«ãå¼ã³åºããããããåã« New Relic agent ãã¢ã¸ã¥ã¼ã«ã® prototype ãæ¸ãæããããããã«ãã¦ãããå¿ è¦ãããã¾ãã
ä»å㯠PrismaClient
ã® prototype ãæ¸ãæããããã« @prisma/client
ã¢ã¸ã¥ã¼ã«ã«å¯¾ã㦠New Relic agent ã«é¢ããå¦çãè¡ãªã£ã¦ããã¾ãã
// register-newrelic.js const newrelic = require("newrelic"); const { instrumentPrisma } = require("./newrelic-prisma"); newrelic.instrumentDatastore("@prisma/client", instrumentPrisma);
ã¨ããã¨ãå¾ã»ã©ä½æãã instrumentPrisma
ã¨ããé¢æ°ã§ @prisma/client
ã¢ã¸ã¥ã¼ã«ã«å¯¾ãã¦å¦çãè¡ããããã«ãªãã¾ãã
instrumentPrisma
é¢æ°ã¯ã第 1 å¼æ°ã« DatastoreShim ã¨ãã New Relic agent ã®ãã«ãã¼çãªãªãã¸ã§ã¯ããåãåãã第 2 å¼æ°ã« @prisma/client
ã¢ã¸ã¥ã¼ã«ãã®ãã®ãåãåãã¾ãã
// newrelic-prisma.js /** * @param {*} shim DatastoreShim ãªãã¸ã§ã¯ãã¨ãªã£ã¦ãã * @param {import("@prisma/client")} prisma * * @see https://newrelic.github.io/node-newrelic/docs/DatastoreShim.html */ export const instrumentPrisma = (shim, prisma) => { // ... };
DatastoreShim ã«ããããã¤ãã®ã¡ã½ããã®ãã¡ã recordQuery
ã¨ããã¡ã½ããã使ç¨ãããã¨ã§ãã¼ã¿ãã¼ã¹ã¸ã®ãªã¯ã¨ã¹ããè¨æ¸¬ãããã¨ãã§ãã¾ãã
ä»å㯠PrismaClient ã® _executeRequest ã«å¯¾ãã¦ãã»ã°ã¡ã³ãã®ä½æãè¡ããããã«ãããã®ã§
// newrelic-prisma.js export const instrumentPrisma = (shim, prisma) => { shim.recordQuery(prisma.PrismaClient.prototype, "_executeRequest", { // ... }); };
ã¨æå®ãã¾ãã
次ã«ã PrismaClient ã® _executeRequest ã«ã¯ SQL ã§ã¯ãªã Prisma ç¬èªã®ãªã¯ã¨ã¹ããªãã¸ã§ã¯ãã渡ãããã®ã§ã newrelic ãããã解éã§ããããã«è¨å®ããã¦ãããå¿ è¦ãããã¾ãã
New Relic agent ã解éããããã«ã¯ collection
ï¼users
ãªã©ã®ãã¼ãã«åã»ã³ã¬ã¯ã·ã§ã³åï¼ã¨operation
ï¼SELECT
ã UPDATE
ãªã©ã®æä½ç¨®å¥ï¼ã query
ï¼å人æ
å ±ãã³ã¡ã³ããªã©ãåãé¤ãããæååã¾ã㯠SQL ãã®ãã®ãä»»æé
ç®ï¼ãæå®ããå¿
è¦ãããã¾ãã
PrismaClient ã® _executeRequest ã«æ¸¡ãããå¼æ°ã®ãã¡ããããã«ä½¿ããããªæ
å ±ãæ¢ããã¨ããã以ä¸ã®ããã«ãããã¨ãã§ãã¾ããã
// newrelic-prisma.js export const instrumentPrisma = (shim, prisma) => { shim.recordQuery(prisma.PrismaClient.prototype, "_executeRequest", { /** * @param {[*]} args _executeRequest ã®å¼æ°ã®é åã§ã第 1 å¼æ°ã« InternalRequestParams ã¨ããåã®ãªãã¸ã§ã¯ãã渡ããã¾ãã * * @see https://github.com/prisma/prisma/blob/6797519fd5c5fc7f523d965e9a55a56c80dc2ee1/packages/client/src/runtime/getPrismaClient.ts#L145-L160 * @see https://github.com/prisma/prisma/blob/6797519fd5c5fc7f523d965e9a55a56c80dc2ee1/packages/client/src/runtime/MiddlewareHandler.ts#L9-L20 */ query: (_0, _1, _2, args) => { const params = args[0]; const query = { // Prismaã®ã¢ãã«åãåå¾ã§ãã collection: params.model, // Prismaã®å ´å㯠findMany ã¨ã findOne ãªã©ã®æ å ±ãåå¾ã§ãã operation: params.action, // SQL ã®ä»£ããã¨ãªããããªæ å ±ãåå¾ãã query: `${params.clientMethod} ${JSON.stringify(params.args)}`, }; // æååã¨ãã¦è¿ãå¿ è¦ããããããä¸åº¦ JSON æååã¸å¤æãã¦ãã¾ãã return JSON.stringify(query); }, }); };
æå¾ã«ã New Relic agent ã§ã¯ããã©ã«ã㧠SQL-like ãªã¯ã¨ãªããã¼ã¹ããä»çµã¿ãããã®ã§ãããããã使ããã«ãã JSON.parse ããããã«è¨å®ãã¾ãã
// newrelic-prisma.js export const instrumentPrisma = (shim, prisma) => { shim.recordQuery(prisma.PrismaClient.prototype, "_executeRequest", { // ... }); shim.setParser((query) => { // query 㯠å ã»ã© JSON.stringify ã§çæããæååã¨ãªã£ã¦ãã return JSON.parse(query); }); };
ããã¾ã§ã§ã»ã¨ãã©ã®è¨å®ãå®äºã§ããæå¾ã«ãã¡ã¤ã«ã®å ¨ä½ãç´¹ä»ãã¾ãã
// register-newrelic.js const newrelic = require("newrelic"); const { instrumentPrisma } = require("./newrelic-prisma"); newrelic.instrumentDatastore("@prisma/client", instrumentPrisma);
// newrelic-prisma.js /** * @param {*} shim DatastoreShim ãªãã¸ã§ã¯ãã¨ãªã£ã¦ãã * @param {import("@prisma/client")} prisma * * @see https://newrelic.github.io/node-newrelic/docs/DatastoreShim.html */ export const instrumentPrisma = (shim, prisma) => { // ãã㧠New Relic ä¸ã§è¡¨ç¤ºãããæååãè¨å®ãããã¨ãã§ãã¾ããä»»æã®æååãè¨å®ãããã¨ãã§ãã¾ãã shim.setDatastore(shim.POSTGRES); shim.recordQuery(prisma.PrismaClient.prototype, "_executeRequest", { query: (_shim, _fn, _name, args) => { const params = args[0]; const query = { collection: params.model, operation: params.action, // SQL ã®ä»£ããã¨ãªããããªæ å ±ãåå¾ãã query: `${params.clientMethod} ${JSON.stringify(params.args)}`, }; return JSON.stringify(query); }, // New Relicä¸ã§ãã¼ã¿ãã¼ã¹ã¨ãã¦ã®æ å ±ã¨ãã¦è¨é²ãã¦ãããããã«æå®ããã¾ãã record: true, // _executeRequest ãéåæé¢æ°ã®ãããããã«é¢ããæå®ããã¦ãã¾ãã promise: true, }); shim.setParser((query) => { return JSON.parse(query); }); };
ããã¦ã register-newrelic.js ãã¡ã¤ã«ã PrismaClient ãåæåãããããåã«èªã¿è¾¼ãã§ããå¿ è¦ãããã¾ããä¸çªç°¡åãªæ¹æ³ã¯
$ node -r register-newrelic.js index.js
ã®ãããªæãã§ãã¢ããªã±ã¼ã·ã§ã³èµ·ååã«èªã¿è¾¼ã¾ãããããã¦ããæ¹æ³ã§ãã
ããã¾ã§è¡ããã¨ã§ã以ä¸ã®ç»åã®ããã« New Relic 㧠Prisma ã®è¨æ¸¬ãè¡ããã¨ãã§ããããã«ãªãã¾ãï¼
æå¾ã«
New Relic 㨠Prisma ãé£æºããããã¨ã§ãã¼ã¿ãã¼ã¹ã¢ã¯ã»ã¹ãå¯è¦åããN+1 ã®æ¤åºãé ãã¯ã¨ãªã®çºè¦ã«ã¤ãªãããã¨ãã§ãã¾ããã ã¹ã¿ãã£ãµããªã§ã¯ããµã¼ãã¹ãå®å®ãã¦ç¶ç¶ãã¦ããããã«ãããã®å é¨æ¹åã«åãçµãã§ãã¾ãã
ããã¦æè²ãGraphQLãTypeScript ãªã©ã«èå³ãããã°ãã²ä¸åº¦ãã«ã¸ã¥ã¢ã«é¢è«ã§ã話ãã¾ãããï¼