Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: rewrite plugin loader #686

Merged
merged 6 commits into from
Mar 23, 2018
Merged

Conversation

kjin
Copy link
Contributor

@kjin kjin commented Mar 8, 2018

This change re-writes the Plugin Loader entirely.

The existing Plugin Loader code exists as a set of module-scope functions and variables. While there aren’t any major problems here, it’s worth re-writing it for the following reasons:

  • To make it more idiomatic to TypeScript, especially now that it has type annotations
  • To make the code more readable
  • To expose a more testable interface (and improve unit tests)
  • To depend on a third party module (require-in-the-middle) for monkeypatching
  • To make future behavioral changes more easy to implement:
    • Support for multiple plugins for the same module
    • Support for plugins that don’t monkeypatch at all

User-facing changes:

  • Intercept<T> is no longer a type on the public interface of the Trace Agent; its definition was merged with Patch<T>. Arguably, they are all "patches". Users will still be warned if they provide both patch and intercept. (Edit: This has been reverted.)
  • We no longer warn if neither patch or intercept exist on a Patch. This is to prevent needless warnings when a module such as knex needs patches for some versions but not others.
  • All log messages from the plugin loader have been revised to be more regex-searchable.
  • We accept an undocumented '[core]' plugin key. A plugin loaded under this key would behave as if the key were any other core module, such as 'http'. The plan is that at some point, we would use the '[core]' key to patch core modules rather than 'http', because it's somewhat misleading that 'http' also patches 'https'.

(All of these are up for discussion.)

Notes for Reviewers

  • This change used to also introduce multiple plugins for a single module. I decided against that since it's (1) too big of a change and (2) unclear whether this makes sense for an end-user. Just something to keep in mind if you see something weird like storing plugins as an array or two layers of version-checks (you should make a note of it if it doesn't make sense).
  • The computed PR diff for trace-plugin-loader.ts is completely useless. I recommend looking at the commits separately -- the first commit is just the removal of the old file.

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Mar 8, 2018
@kjin kjin force-pushed the plugin-loader-rewrite branch 2 times, most recently from b210598 to 64b0bf2 Compare March 15, 2018 20:00
@codecov
Copy link

codecov bot commented Mar 15, 2018

Codecov Report

Merging #686 into master will increase coverage by 1.09%.
The diff coverage is 92.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #686      +/-   ##
==========================================
+ Coverage   89.45%   90.54%   +1.09%     
==========================================
  Files          29       29              
  Lines        1422     1439      +17     
  Branches      287      285       -2     
==========================================
+ Hits         1272     1303      +31     
+ Misses         66       57       -9     
+ Partials       84       79       -5
Impacted Files Coverage Δ
src/plugins/plugin-knex.ts 92.85% <ø> (ø) ⬆️
src/index.ts 90.14% <100%> (+0.14%) ⬆️
src/util.ts 95.45% <100%> (+2.59%) ⬆️
src/trace-plugin-loader.ts 92.56% <92.41%> (+6.6%) ⬆️
src/plugins/plugin-mysql2.ts 72.97% <0%> (-10.82%) ⬇️
src/trace-writer.ts 88.52% <0%> (+0.81%) ⬆️
src/plugins/plugin-http.ts 90.24% <0%> (+10.97%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dca5cc0...eb986a3. Read the comment docs.

@kjin kjin force-pushed the plugin-loader-rewrite branch 6 times, most recently from 57ae381 to e3f0558 Compare March 19, 2018 19:59
@kjin kjin force-pushed the plugin-loader-rewrite branch from e3f0558 to 5cc7dad Compare March 19, 2018 20:47
@kjin kjin changed the title feat: allow multiple plugins for a single module refactor: rewrite plugin loader Mar 19, 2018
@kjin kjin force-pushed the plugin-loader-rewrite branch 6 times, most recently from a64a8e2 to f330840 Compare March 20, 2018 00:24
@kjin kjin changed the title refactor: rewrite plugin loader fix: rewrite plugin loader Mar 20, 2018
@kjin kjin force-pushed the plugin-loader-rewrite branch 2 times, most recently from cc98657 to 8fa83cf Compare March 20, 2018 00:50
@kjin kjin requested review from ofrobots, jinwoo and DominicKramer and removed request for ofrobots, jinwoo and DominicKramer March 20, 2018 00:55
@kjin kjin force-pushed the plugin-loader-rewrite branch 2 times, most recently from 5badda4 to 019db1f Compare March 20, 2018 17:57
@kjin kjin force-pushed the plugin-loader-rewrite branch from 019db1f to 6f73473 Compare March 20, 2018 20:23
Copy link
Contributor

@jinwoo jinwoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still reviewing. Sending some comments first.

src/index.ts Outdated
pluginLoader.deactivate();
try {
const loader = pluginLoader.get();
loader.deactivate();

This comment was marked as spam.

src/index.ts Outdated
try {
const loader = pluginLoader.get();
loader.deactivate();
} catch (e) {

This comment was marked as spam.

export type Instrumentation<T> = Patch<T>|Intercept<T>;

export type Plugin = Array<Instrumentation<any>>;
export type Plugin = Array<Partial<Patch<any>>>;

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

export class ModulePluginWrapper implements PluginWrapper {
// Sentinel value to indicate that a plugin has not been loaded into memory
// yet.
private static readonly NOT_LOADED: Plugin = [];

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

// ranges
let numFiles = 0;
plugin.forEach(instrumentation => {
const postLoadVersions = instrumentation.versions;

This comment was marked as spam.

This comment was marked as spam.

}

unapplyAll() {
this.unpatchFns.reverse().forEach(fn => fn());

This comment was marked as spam.

this.unpatchFns.push(() => {
this.logger.info(
`PluginWrapper#unapplyAll: [${logString}] Unpatching file.`);
instrumentation.unpatch!(exportedValue);

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@jinwoo jinwoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't look at test code very closely.

logger.error('Plugins activated more than once.');
return;
/**
* A class that represents wrapper logic on top of plugins that patch core

This comment was marked as spam.

This comment was marked as spam.

*/
applyPlugin<T>(moduleExports: T, file: string, version: string): T {
return this.children.reduce((exportedValue, child) => {
return child.applyPlugin(exportedValue, file, version);

This comment was marked as spam.

*/
export class PluginLoader {
// Key on which core modules are stored.
static readonly CORE_MODULE = '[core]';

This comment was marked as spam.

* @param config The configuration for this instance.
*/
constructor(logger: Logger, config: PluginLoaderConfig) {
this.logger = logger;

This comment was marked as spam.

const value = config.plugins[key];
// Core module plugins share a common key.
const coreModule = builtinModules.indexOf(key) !== -1 ||
key === PluginLoader.CORE_MODULE;

This comment was marked as spam.

This comment was marked as spam.

* loaded or applied, as well as unpatching any modules for which plugins
* specified an unpatching method.
*/
deactivate(): PluginLoader {

This comment was marked as spam.

* Adds a search path for plugin modules. Intended for testing purposes only.
* @param searchPath The path to add.
*/
static setPluginSearchPath(searchPath: string) {

This comment was marked as spam.

This comment was marked as spam.

const parts = moduleStr.replace(/\\/g, '/').split('/');
let indexOfFile = 1;
if (parts[0].startsWith('@')) { // for @org/package
indexOfFile = 2;

This comment was marked as spam.

This comment was marked as spam.

@@ -1,5 +1,5 @@
/**
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2018 Google Inc. All Rights Reserved.

This comment was marked as spam.

This comment was marked as spam.

'module-h': 'module-h-plugin'
}

it('doesn\'t unpatch twice', () => {

This comment was marked as spam.

This comment was marked as spam.

@kjin kjin force-pushed the plugin-loader-rewrite branch from 065bc5b to d7d46a0 Compare March 21, 2018 00:14
@kjin kjin force-pushed the plugin-loader-rewrite branch from d7d46a0 to 11a0fc9 Compare March 22, 2018 18:37
@@ -1,4 +1,5 @@
node_modules/
!test/fixtures/loader/node_modules/

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

export type Instrumentation<T> = Patch<T>|Intercept<T>;

export type Plugin = Array<Instrumentation<any>>;
export type Plugin = Array<Partial<Patch<any>>>;

This comment was marked as spam.

@@ -18,7 +18,6 @@
var shimmer = require('shimmer');
var util = require('util');

// knex 0.10.x and 0.11.x do not need patching

This comment was marked as spam.

This comment was marked as spam.

@kjin kjin force-pushed the plugin-loader-rewrite branch from 11a0fc9 to 59644b1 Compare March 22, 2018 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants