A troubleshooting process for pure online interface exceptions

background

An exception occurs in the online interface, and the abnormal problem cannot be reproduced in both the test environment and the local environment.

technology stack

Front-end umi + antd, back-end egg + egg-sequelize, the main troubleshooting direction is the back-end.

Start troubleshooting

Start troubleshooting the exception, and the exception interface returns no detailed error information. The error message returned is only a simple error prompt 其他异常, which is the default prompt for interface exceptions.

EXCEPTION_MSG : 'Other exceptions'

However, when the interface is abnormal, specific exception information will be passed in, but it becomes the default value in the front end. It can be seen that one is passed in here undefined.

try {
  ...code
} catch (e) {
   return ctx. EXCEPTION (e && e. toString ())
}

After investigation, it was found that the corresponding error content try catchwas not returned in one of the exceptions in the logic processing , resulting in the default value being returned to the front end.reject

return  new  Promise ( async (resolve, reject) => {
   try {
   ...code
  } catch (error) {
-reject    ( )
+    reject (error)
  }
})

After processing, an error exception message appears, indicating that the regular expression is invalid. According to the source of the error prompt, the corresponding business code function is found, but no regular expression-related code is used in this function.

SyntaxError : Invalid regular expression : /^:(?<name>[a-z_][0-9a-z_]*)(?:\)|,|$|\s|::|;|])/ : Invalid group
  at injectReplacements ( /opt/ web/node/xxx/node_modules/sequelize/lib/utils/sql. js : 120 : 37 )
  at Sequelize . query ( /opt/ web/node/xxx/node_modules/sequelize/lib/sequelize. js : 282 : 13 )
  at Promise ( /opt/ web/node/xxx/app/service/sentry/xxx. js : 628 : 45 )
  at new  Promise (<anonymous>)
  ...

Because this function calls other business functions, the code that caused the exception can be found out through log printing as follows. Here is a sqlquery. Because the order of the query fields needs to be consistent with the returned list, it is used replacementsbecause it is normal in other environments. , to eliminate this syntax problem.

await  this . model . query (sql, {
   replacements : { name : sortList },
   type : QueryTypes . SELECT 
})

Going back to the calling relationship where the exception was thrown above, after calling the business code, it called , in sequence Sequelize.queryinjectReplacementsIf an exception occurs, the problem lies injectReplacements. However, the call Sequelize.querydoes not appear when checking the local source code injectReplacements. The source code only has the following processing for replacementsthe configuration, which is a bit strange.

if (options. replacements ) {
   if ( Array . isArray (options. replacements )) {
    sql = Utils . format ([sql]. concat (options. replacements ), this . options . dialect );
  } else {
    sql = Utils . formatNamedParameters (sql, options . replacements , this . options . dialect );
  }
}

Since the local code cannot be found, then go and search the source code on the server. As expected, the source code on the server is actually inconsistent.

if (options.replacements ) {
  sql = injectReplacements (sql, this . dialect , options. replacements );
}

injectReplacementsThe following regular rules were finally called in the function, which is the exception prompt content at the beginning of this article, indicating that the regular rules are invalid.

const match = remainingString. match ( /^:(?<name>[a-z_][0-9a-z_]*)(?:\)|,|$|\s|::|;|])/ i );
 const replacementName = (_d = match == null ? void  0 : match. groups ) == null ? void  0 : _d. name ;
 if (!replacementName) {
   continue ;
}

Then I checked sequelizethe version numbers of the two environment dependency packages respectively. The local environment is sequelize@6.16.1, and the actual installed version of the online environment is sequelize@6.21.3.

"_from" : "sequelize@^6.0.0" ,
 "_id" : "sequelize@6.21.3" ,

Since it is a version issue, unify the version number and see if it can be reproduced locally. Because this dependency is not a direct dependency package, the version cannot be directly locked, so delete the local version first node_modulesand package-lock.jsonthen reinstall it. The final installed version number is consistent with the server , both sequelize@6.21.3, but the local operation is normal at this time. 🤷‍♀️

But why does such a problem occur? Wasn’t it always fine before? The reason is that the dependent sequelizeversion number is not locked and sequelizehas been continuously upgraded from the beginning of the project to the present. After checking the official website github, it was found that due to an sqlinjection problem, 6.19.2this problem has been fixed since the version. As a result, the regular problem cannot be recognized in older nodeversions and exceptions occur. github issue address: https://github.com/sequelize/…

solve

This problem can be solved in two ways, because the dependent package where the exception occurs is not a direct dependent package, and the corresponding version number cannot be written directly. Then you can modify package-lockthe version number of the dependent package in the file, but this method is not stable. Later installation will still be overwritten. The second way is to upgrade nodethe version. Because the company has many internal server projects, it requires a certain degree of test regression coverage, which involves certain risks and costs.

at last

This article ends here. Two conclusions can be drawn from this troubleshooting of online problems. Interface exceptions do not return specific error messages. You need to pay attention to code problems in the future to improve the efficiency of solving exceptions. When troubleshooting, you must ensure a copy of the code while also ensuring the consistency of system versions and dependency versions in different environments. For dependencies The version number of the package is locked as much as possible to avoid unknown risks caused by automatic upgrades.