Skip to content

Latest commit

 

History

History
176 lines (127 loc) · 5.34 KB

index.md

File metadata and controls

176 lines (127 loc) · 5.34 KB

Tw-EntryPoints documentation

Table of Contents

Intro

Provides various metrics for service's databases usage.

Is built on top of

Integrates nicely with Transferwise Entrypoints system.

Example dashboards:

Setup

Assuming, your data sources are using HikariCP, only thing you need to do, is to add a dependency.

runtimeOnly "com.transferwise.common:tw-entrypoints-starter"

For best results, it is also recommended to integrate with TW Service Comms.

Integration tests

You can also use Database Access Statistics in test code, for example to verify that your hibernate magic code does not have any N+1 problem:

unitOfWorkManager.createEntryPoint("test", "test").toContext().execute({
  mvcResult = getMockMvc()
      .perform(post("/v1/batchPayouts/getBatchList")
          .contentType(MediaType.APPLICATION_JSON)
          .content(jsonConverter.fromObject(request)))
      .andReturn()
  dbStats = DatabaseAccessStatistics.get("payout")

  assert dbStats.getTransactionalQueriesCount() == 1
  assert dbStats.getNonTransactionalQueriesCount() == 0
})

You would need to add a dependency for this as well:

testImplementation "com.transferwise.common:tw-entrypoints"

Table access statistics and JSqlParser library

We are using JSqlParser library to parse table names from queries.

The library is pretty good, but some services have few queries, it can not parse. Also, sometimes the parsing can take so long, that it will create latency spikes or cpu burns.

In those case, you can override/control the parsing via TasQueryParsingInterceptor and TasParsedQueryRegistry.

Example for TasQueryParsingInterceptor.

@Component
public class MyTasQueryParsingInterceptor extends DefaultTasQueryParsingInterceptor {

  @Override
  public InterceptResult intercept(String sql) {
    if (StringUtils.startsWithIgnoreCase(sql, "SET fancy_variable TO")) {
      return InterceptResult.doSkip();
    }

    else if (sql.equals(knownUnParseableSql)){
      return InterceptResult.returnParsedQuery(new ParsedQuery()
          .addOperation("insert", new ParsedQuery.SqlOperation()
              // Main table should always be first, as we register "first-table" metrics by that.
              .addTable("transfer")
              .addTable("payout")));
    }

    return super.intercept(sql);
  }
}

Example for TasParsedQueryRegistry.

@Autowired
private TasParsedQueryRegistry registry;

public void registerBadSqls(){
    registry.register(knownUnParseableSql,new ParsedQuery()
      .addOperation("insert", new ParsedQuery.SqlOperation()
         .addTable("transfer")
         .addTable("payout")));
}

In case where failed parsing will create too much logs noise, you have an option to override TasQueryParsingListener.

For example:

public class MyTasQueryParsingListener extends DefaultTasQueryParsingListener {

  @Override
  public void parsingFailed(String sql, Duration timeTaken, Throwable t) {
    if (sql.equals(knownProblematicQuery)) {
      // ignore
    } else {
      super.parsingFailed(sql, timeTaken, t);
    }
  }
}

The library is automatically disabling query parsing during Flyway migration, but only when you use Spring Boot autoconfiguration to setup Flyway.

If you create the Flyway bean manually, then run the FluentConfiguration through flyway customizers.

For example.

@Bean
Flyway flyway(@Qualifier("dataSource") DataSource dataSource, List<FlywayConfigurationCustomizer> flywayConfigurationCustomizers) {
    FluentConfiguration config = Flyway.configure()... ;

    for (var flywayConfigurationCustomizer : flywayConfigurationCustomizers){
        flywayConfigurationCustomizer.customize(config);
    }

    return new Flyway(config);
}

When you would need to disable the query parsing for specific queries, or routine, surround those with TwContext and use TasUtils.

E.g.

TwContext.current().createSubContext().execute(() -> {
  TasUtils.disableQueryParsing(TwContext.current());
  jdbcTemplate.update("alter update create blah");
});

License

Copyright 2021 TransferWise Ltd.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.