Reusable Composition of Functional Hooks for Error Handling, Log, Metrics and More

栏目: IT技术 · 发布时间: 4年前

内容简介:There're a large amount of semi-automatable codes in most production codebases, especially around input validation/null check, error/exception handling, observability anchors(log, metrics, tracing) and various other elements to thread functions together to

toolchain

:snowboarder: the swiss knife to lift business logic to be production ready

Purpose

There're a large amount of semi-automatable codes in most production codebases, especially around input validation/null check, error/exception handling, observability anchors(log, metrics, tracing) and various other elements to thread functions together to achieve business goals stably. All those are essential for production code, while they are slowly corrupting the readability/maintability of the codebase, incuring huge communication cost between teams due to a lack of common standards. Fortunately, without AI, it is still possible to automate some of those common programming actions with a standard.

With the power of function composition in Javascript, it becomes very simple to modularize those control mechanisms in the form of well-tested reusable decorators. This makes the core business logic functions extremely conscise and easy to read/test/migrate.

How to Use

Install

yarn add @opbi/toolchain

Concepts & Conventions

A few concepts and conventions are introduced to make a standard for those decorators to work well with each other. We'll call a core logic function action , and it comes with a standard function signature of (param<object>, meta<object>, context<object>) . param is the arg of the input values to the core logic action, and with object destruct assigning it is a best-practice to make function calls. meta is the arg to be used primarily by logger, metrics clients to anchor meta-data of the function calls to provide the background where and how this call happened. context is the arg where you append instances of e.g. logger, metrics and other functions down to the action calling chain.

The decorators come with a standard config step decorator(config)(action) , and are named in the format of [hook point]-[client/behaviour] , e.g. errorCounter, eventLogger .

There are generally three hook points: before, after, error , and when a decorator is hooked into both after and error, we call it event .

Compose & Reuse

Say we're going to build a simple module to parse and upload a list of json files to a database. Using the decorators, we can write minimum code in json-file-reader.js and db-batch-write.js just to cover the essential logics and get them tested. Then assemble those step actions in the main module.

//json-to-db.js
import {
  errorCounter,
  errorMute,
  errorRetry,
  eventLogger,
  eventTimer,
  recompose,
} from '@opbi/toolchain/dist/decorators';

import jsonFileReader from './json-file-reader';
import dbBatchWrite from './db-batch-write';

const actionErrorLessThan = number =>
  (e, p, m, { metrics }, action) =>
    metrics &&
    metrics.find({ action, type: 'error' }).value() < number;

const labelBatchSize = (p, m, c) => ({ batchSize: c.batch.size });

const jsonToDb = async ({ fileList }, meta, context) => {
  const data = await recompose(
    errorMute({ condition: actionErrorLessThan(20) }),
    eventTimer(),
    eventLogger(),
    errorCounter(),
  )(jsonFileReader)({ fileList }, meta, context);

  await recompose(
    eventTimer({ parseLabel: labelBatchSize }),
    errorRetry(),
    eventLogger(),
    errorCounter(),
    errorMute({ condition: e => e.code === 11000}),
  )(dbBatchWrite)({ data }, meta, context);
};

export default jsonToDb;

The order of the decorators in the recompose chain matters.

The afterHooks and errorHooks are from bottom to top, while the beforeHooks will be executed from top to bottom and meta/context would be passed from top to bottom.

In the 1st recompose, because of the errorMute on the top position, before the number of errors counted by errorCounter reaches 20, any error populated from jsonFileReader would be muted so that jsonToDb process is not stopped.

In the 2nd recompose above error with code 11000 would be muted and will not be populated to errorCounter , eventLogger , errorRetry , eventTimer . The eventTimer at the very top would time in the whole duration of execution even if multiple retries happened.

You can see from above that, we can actually make decorator config function with names to make the behaviour highly descriptive. This makes it even possible to package and reuse function control patterns, which is a very efficient way to leverage the power of meta-programming.

//patterns/observerable-retry.js
import {
  errorCounter,
  errorRetry,
  eventLogger,
  eventTimer,
  recompose,
} from '@opbi/toolchain/dist/decorators';

export default recompose(
  eventTimer(),
  errorRetry(),
  eventLogger(),
  errorCounter(),
);

Error Handling

Furthermore, this makes error handling very handy combing custom hanlders with universal handler.

//cancel-subscription.js
import {
  errorHandler,
  errorRetry,
  recompose,
} from '@opbi/toolchain/dist/decorators';

import userProfileApi from './api/user-profile';
import subscriptionApi from './api/subscription';

import universalErrorPage from './handler/error-page';

const errorForbiddenHandler = {
  condition: e => e.code === 403,
  handler: (e, p, m, { res }) => res.status(403).redirect('/');
}

const timeoutErrorRetry = errorRetry({
  condition: e => e.type === 'TimeoutError'
});

const cancelSubscription = async ({ userId }, meta, context) => {
  const { subscriptionId } = await recompose(
    errorHandler({ condition: e => e.code !== 403, handler: universalErrorPage })
    errorHandler(errorForbiddenHandler),
    timeoutErrorRetry
  )(userProfileApi.getSubscription)(
    { userId }, meta, context
  );

  await recompose(
    errorHandler({
      condition: e => e.code > 500,
      handler: (e, { subscriptionId }, m, c) => subscriptionApi.restore({ subscriptionId }, m, c),
    }),
    timeoutErrorRetry,
  )(subscriptionApi.cancel)({ subscriptionId }, meta, context);
};

export default cancelSubscription;

Ecosystem

Currently available decorators are as following:

Extension

You can also create more decorators for yourself or the ecosystem with the reliable standard decorator creator(coming soon). It helps you to maintain a standard across your decorators to ensure compatibility and consistent develop experience.

License

MIT


以上所述就是小编给大家介绍的《Reusable Composition of Functional Hooks for Error Handling, Log, Metrics and More》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人人都是架构师:分布式系统架构落地与瓶颈突破

人人都是架构师:分布式系统架构落地与瓶颈突破

高翔龙 / 电子工业出版社 / 2017-5 / 69

《人人都是架构师:分布式系统架构落地与瓶颈突破》并没有过多渲染系统架构的理论知识,而是切切实实站在开发一线角度,为各位读者诠释了大型网站在架构演变过程中出现一系列技术难题时的解决方案。《人人都是架构师:分布式系统架构落地与瓶颈突破》首先从分布式服务案例开始介绍,重点为大家讲解了大规模服务化场景下企业应该如何实施服务治理;然后在大流量限流/消峰案例中,笔者为大家讲解了应该如何有效地对流量实施管制,避......一起来看看 《人人都是架构师:分布式系统架构落地与瓶颈突破》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试