embulk-output-multi 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 40d2c979fbce0853ed920cae18f0f867284e9491
4
- data.tar.gz: e02bef302e6b8061f9029dc4d7077a00b7563623
3
+ metadata.gz: d23fe45b21d16664727849db0d1af9015a180834
4
+ data.tar.gz: 5b3dbb7047049be9dd3784cafb93855e1288fc36
5
5
  SHA512:
6
- metadata.gz: ba665f2f4108d65c1cf48ad5a94789e6d719443bb0e8bc7f579f05357407918ab275b41cd55aa96af0ae0854e572427f55039a8502e332c8ab22d6707d6118f9
7
- data.tar.gz: 7fa76fd0c1a8c06e89b2b0eff90e6b16e00985db269444a37812da6af0f40e3a74a748b00c7e3dfc675b35f7c60875e5f57efd87bf6963eacf57f05cc04a9e79
6
+ metadata.gz: 0fe2a6a708a3602be1e8512eba3d2ab3d9e4fd789ce833a0f5872f7c9790adc4112463d0ed5f84e45224bff331ef2594c4cff9596288c91be8144e04ce7ad7a9
7
+ data.tar.gz: 6b66ca734eb9786edd92632ede8678c2152a22b4ffa668215ea12d3ebc9250364582c0399e30ab130ce7ab2ff8de4dd245b34b8b61e2c10ef30ed34656ece096
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Multi output plugin for Embulk
2
2
 
3
- This plugin can copies an output to multiple destinations.
3
+ This plugin copies an output to multiple destinations.
4
4
 
5
5
  ### Notes
6
6
  - It's still very experimental version, so might change its configuration names or styles without notification.
7
- - It might have performance issues or bugs when loading large records.
8
- - It might not working on other executors than LocalExecutor.
7
+ - As this plugin performs multiple output methods, it might have a performance issue with large records.
8
+ - It might not work on other executors than LocalExecutor.
9
9
  - I would appreciate it if you use this and give me reports/feedback!
10
10
 
11
11
  ## Overview
@@ -27,13 +27,13 @@ out:
27
27
  outputs:
28
28
  # Output to stdout
29
29
  - type: stdout
30
- # Output to file as CSV
30
+ # Output to files as CSV
31
31
  - type: file
32
32
  path_prefix: out_file_
33
33
  file_ext: csv
34
34
  formatter:
35
35
  type: csv
36
- # Output to file as TSV
36
+ # Output to files as TSV
37
37
  - type: file
38
38
  path_prefix: out_file_
39
39
  file_ext: tsv
@@ -1,5 +1,5 @@
1
1
  plugins {
2
- id "com.github.kamatama41.embulk" version "0.4.0"
2
+ id "com.github.kamatama41.embulk" version "0.5.2"
3
3
  id "net.researchgate.release" version "2.8.0"
4
4
  }
5
5
 
@@ -17,8 +17,10 @@ configurations {
17
17
  }
18
18
 
19
19
  dependencies {
20
- testImplementation "org.embulk:embulk-standards:0.9.15"
21
- testImplementation "org.embulk:embulk-test:0.9.15"
20
+ testImplementation "org.embulk:embulk-deps-buffer:0.9.23"
21
+ testImplementation "org.embulk:embulk-deps-config:0.9.23"
22
+ testImplementation "org.embulk:embulk-standards:0.9.23"
23
+ testImplementation "org.embulk:embulk-test:0.9.23"
22
24
  testImplementation "com.github.kamatama41:embulk-test-helpers:0.7.4"
23
25
  testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.2"
24
26
  testImplementation "org.junit.jupiter:junit-jupiter-engine:5.3.2"
@@ -29,7 +31,7 @@ test {
29
31
  }
30
32
 
31
33
  embulk {
32
- version = "0.9.15"
34
+ version = "0.9.23"
33
35
  category = "output"
34
36
  name = "multi"
35
37
  authors = ["Shinichi ISHIMURA"]
@@ -1 +1 @@
1
- version=0.2.1
1
+ version=0.5.0
@@ -9,27 +9,17 @@ com.fasterxml.jackson.datatype:jackson-datatype-guava:2.6.7
9
9
  com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
10
10
  com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
11
11
  com.fasterxml.jackson.module:jackson-module-guice:2.6.7
12
- com.google.code.findbugs:annotations:3.0.0
13
12
  com.google.guava:guava:18.0
14
13
  com.google.inject.extensions:guice-multibindings:4.0
15
14
  com.google.inject:guice:4.0
16
- com.ibm.icu:icu4j:54.1.1
17
15
  commons-beanutils:commons-beanutils-core:1.8.3
18
- commons-cli:commons-cli:1.3.1
19
- commons-collections:commons-collections:3.2.1
20
- commons-lang:commons-lang:2.4
21
- io.airlift:slice:0.9
22
- io.netty:netty-buffer:4.0.44.Final
23
- io.netty:netty-common:4.0.44.Final
24
16
  javax.inject:javax.inject:1
25
17
  javax.validation:validation-api:1.1.0.Final
26
18
  joda-time:joda-time:2.9.2
27
19
  org.apache.bval:bval-core:0.5
28
20
  org.apache.bval:bval-jsr303:0.5
29
21
  org.apache.commons:commons-lang3:3.4
30
- org.apache.velocity:velocity:1.7
31
- org.embulk:embulk-core:0.9.15
22
+ org.embulk:embulk-core:0.9.23
32
23
  org.jruby:jruby-complete:9.1.15.0
33
24
  org.msgpack:msgpack-core:0.8.11
34
25
  org.slf4j:slf4j-api:1.7.12
35
- org.yaml:snakeyaml:1.18
@@ -10,15 +10,10 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.6.7
10
10
  com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.7
11
11
  com.fasterxml.jackson.module:jackson-module-guice:2.6.7
12
12
  com.github.kamatama41:embulk-test-helpers:0.7.4
13
- com.google.code.findbugs:annotations:3.0.0
14
13
  com.google.guava:guava:18.0
15
14
  com.google.inject.extensions:guice-multibindings:4.0
16
15
  com.google.inject:guice:4.0
17
- com.ibm.icu:icu4j:54.1.1
18
16
  commons-beanutils:commons-beanutils-core:1.8.3
19
- commons-cli:commons-cli:1.3.1
20
- commons-collections:commons-collections:3.2.1
21
- commons-lang:commons-lang:2.4
22
17
  io.airlift:slice:0.9
23
18
  io.netty:netty-buffer:4.0.44.Final
24
19
  io.netty:netty-common:4.0.44.Final
@@ -30,11 +25,12 @@ org.apache.bval:bval-core:0.5
30
25
  org.apache.bval:bval-jsr303:0.5
31
26
  org.apache.commons:commons-compress:1.10
32
27
  org.apache.commons:commons-lang3:3.4
33
- org.apache.velocity:velocity:1.7
34
28
  org.apiguardian:apiguardian-api:1.0.0
35
- org.embulk:embulk-core:0.9.15
36
- org.embulk:embulk-standards:0.9.15
37
- org.embulk:embulk-test:0.9.15
29
+ org.embulk:embulk-core:0.9.23
30
+ org.embulk:embulk-deps-buffer:0.9.23
31
+ org.embulk:embulk-deps-config:0.9.23
32
+ org.embulk:embulk-standards:0.9.23
33
+ org.embulk:embulk-test:0.9.23
38
34
  org.hamcrest:hamcrest-core:1.3
39
35
  org.hamcrest:hamcrest-library:1.3
40
36
  org.jruby:jruby-complete:9.1.15.0
@@ -5,45 +5,46 @@ import org.embulk.config.TaskReport;
5
5
  import org.embulk.config.TaskSource;
6
6
  import org.embulk.spi.OutputPlugin;
7
7
 
8
- import java.util.ArrayList;
9
8
  import java.util.List;
10
9
  import java.util.concurrent.Callable;
10
+ import java.util.concurrent.ConcurrentHashMap;
11
+ import java.util.concurrent.ConcurrentMap;
11
12
  import java.util.concurrent.CountDownLatch;
12
13
  import java.util.concurrent.ExecutionException;
13
14
  import java.util.concurrent.ExecutorService;
14
15
  import java.util.concurrent.Executors;
15
16
  import java.util.concurrent.Future;
16
- import java.util.concurrent.atomic.AtomicReferenceArray;
17
17
 
18
18
  class AsyncRunControl {
19
19
  private static final String THREAD_NAME_FORMAT = "multi-run-control-%d";
20
20
  private final MultiOutputPlugin.PluginTask task;
21
21
  private final OutputPlugin.Control control;
22
22
  private final CountDownLatch latch;
23
- private final AtomicReferenceArray<TaskSource> taskSources;
23
+ private final ConcurrentMap<String, TaskSource> taskSources;
24
24
  private final ExecutorService executorService;
25
- private Future<List<TaskReport>> result;
25
+ private final Future<List<TaskReport>> result;
26
26
 
27
27
  static AsyncRunControl start(MultiOutputPlugin.PluginTask task, OutputPlugin.Control control) {
28
- return new AsyncRunControl(task, control).start();
28
+ return new AsyncRunControl(task, control);
29
29
  }
30
30
 
31
31
  private AsyncRunControl(MultiOutputPlugin.PluginTask task, OutputPlugin.Control control) {
32
32
  this.task = task;
33
33
  this.control = control;
34
34
  this.latch = new CountDownLatch(task.getOutputConfigs().size());
35
- this.taskSources = new AtomicReferenceArray<>(task.getOutputConfigs().size());
35
+ this.taskSources = new ConcurrentHashMap<>(task.getOutputConfigs().size());
36
36
  this.executorService = Executors.newSingleThreadExecutor(
37
37
  new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_FORMAT).build()
38
38
  );
39
+ this.result = executorService.submit(new RunControl());
39
40
  }
40
41
 
41
42
  void cancel() {
42
43
  result.cancel(true);
43
44
  }
44
45
 
45
- void addTaskSource(int index, TaskSource taskSource) {
46
- taskSources.set(index, taskSource);
46
+ void addTaskSource(String tag, TaskSource taskSource) {
47
+ taskSources.putIfAbsent(tag, taskSource);
47
48
  latch.countDown();
48
49
  }
49
50
 
@@ -55,19 +56,10 @@ class AsyncRunControl {
55
56
  }
56
57
  }
57
58
 
58
- private AsyncRunControl start() {
59
- this.result = executorService.submit(new RunControl());
60
- return this;
61
- }
62
-
63
59
  private class RunControl implements Callable<List<TaskReport>> {
64
60
  @Override
65
61
  public List<TaskReport> call() throws Exception {
66
62
  latch.await();
67
- List<TaskSource> taskSources = new ArrayList<>(AsyncRunControl.this.taskSources.length());
68
- for (int i = 0; i < AsyncRunControl.this.taskSources.length(); i++) {
69
- taskSources.add(AsyncRunControl.this.taskSources.get(i));
70
- }
71
63
  task.setTaskSources(taskSources);
72
64
  return control.run(task.dump());
73
65
  }
@@ -9,21 +9,21 @@ import org.embulk.config.Task;
9
9
  import org.embulk.config.TaskReport;
10
10
  import org.embulk.config.TaskSource;
11
11
  import org.embulk.plugin.PluginType;
12
- import org.embulk.spi.Buffer;
13
12
  import org.embulk.spi.Exec;
14
13
  import org.embulk.spi.ExecSession;
15
14
  import org.embulk.spi.OutputPlugin;
16
- import org.embulk.spi.Page;
17
15
  import org.embulk.spi.Schema;
18
16
  import org.embulk.spi.TransactionalPageOutput;
17
+ import org.slf4j.Logger;
18
+ import org.slf4j.LoggerFactory;
19
19
 
20
20
  import java.util.ArrayList;
21
+ import java.util.HashMap;
21
22
  import java.util.List;
23
+ import java.util.Map;
22
24
  import java.util.Optional;
23
25
  import java.util.concurrent.ExecutionException;
24
- import java.util.concurrent.Future;
25
26
  import java.util.function.Function;
26
- import java.util.stream.Collectors;
27
27
 
28
28
  public class MultiOutputPlugin implements OutputPlugin {
29
29
  public interface PluginTask extends Task {
@@ -32,12 +32,13 @@ public class MultiOutputPlugin implements OutputPlugin {
32
32
 
33
33
  @Config(CONFIG_NAME_OUTPUT_CONFIG_DIFFS)
34
34
  @ConfigDefault("null")
35
- Optional<List<ConfigDiff>> getOutputConfigDiffs();
35
+ Optional<Map<String, ConfigDiff>> getOutputConfigDiffs();
36
36
 
37
- List<TaskSource> getTaskSources();
38
- void setTaskSources(List<TaskSource> taskSources);
37
+ Map<String, TaskSource> getTaskSources();
38
+ void setTaskSources(Map<String, TaskSource> taskSources);
39
39
  }
40
40
 
41
+ private static final Logger LOGGER = LoggerFactory.getLogger(MultiOutputPlugin.class);
41
42
  private static final String CONFIG_NAME_OUTPUT_CONFIG_DIFFS = "output_config_diffs";
42
43
  static final String CONFIG_NAME_OUTPUT_TASK_REPORTS = "output_task_reports";
43
44
 
@@ -45,7 +46,7 @@ public class MultiOutputPlugin implements OutputPlugin {
45
46
  public ConfigDiff transaction(ConfigSource config, Schema schema, int taskCount, OutputPlugin.Control control) {
46
47
  final PluginTask task = config.loadConfig(PluginTask.class);
47
48
  if (task.getOutputConfigs().isEmpty()) {
48
- throw new ConfigException("'outputs' must have more than than or equals to 1 element.");
49
+ throw new ConfigException("'outputs' must have more than or equals to 1 element.");
49
50
  }
50
51
  final ExecSession session = Exec.session();
51
52
  final AsyncRunControl runControl = AsyncRunControl.start(task, control);
@@ -78,78 +79,15 @@ public class MultiOutputPlugin implements OutputPlugin {
78
79
  public TransactionalPageOutput open(TaskSource taskSource, Schema schema, int taskIndex) {
79
80
  final PluginTask task = taskSource.loadTask(PluginTask.class);
80
81
  final ExecSession session = Exec.session();
81
- final List<TransactionalPageOutputDelegate> delegates = mapWithPluginDelegate(task, session, delegate ->
82
- new TransactionalPageOutputDelegate(taskIndex, delegate, delegate.open(schema, taskIndex))
83
- );
84
-
85
- return new TransactionalPageOutput() {
86
- @Override
87
- public void add(Page original) {
88
- final Buffer originalBuffer = original.buffer();
89
- for (TransactionalPageOutputDelegate output : delegates) {
90
- final Buffer copiedBuffer = Buffer.wrap(originalBuffer.array());
91
- copiedBuffer.offset(originalBuffer.offset());
92
- copiedBuffer.limit(originalBuffer.limit());
93
-
94
- final Page copiedPage = Page.wrap(copiedBuffer);
95
- copiedPage.setStringReferences(new ArrayList<>(original.getStringReferences()));
96
- copiedPage.setValueReferences(new ArrayList<>(original.getValueReferences()));
97
-
98
- output.add(copiedPage);
99
- }
100
- }
101
-
102
- @Override
103
- public void finish() {
104
- for (TransactionalPageOutputDelegate output : delegates) {
105
- output.finish();
106
- }
107
- }
108
-
109
- @Override
110
- public void close() {
111
- for (TransactionalPageOutputDelegate output : delegates) {
112
- output.close();
113
- }
114
- }
115
-
116
- @Override
117
- public void abort() {
118
- for (TransactionalPageOutputDelegate output : delegates) {
119
- output.abort();
120
- }
121
- }
122
-
123
- @Override
124
- public TaskReport commit() {
125
- final TaskReport report = Exec.newTaskReport();
126
- final List<TaskReport> reports = new ArrayList<>();
127
- final List<OutputPluginDelegate> errorPlugins = new ArrayList<>();
128
- for (TransactionalPageOutputDelegate output : delegates) {
129
- try {
130
- reports.add(output.commit());
131
- } catch (PluginExecutionException e) {
132
- errorPlugins.add(e.getPlugin());
133
- }
134
- }
135
- if (!errorPlugins.isEmpty()) {
136
- throw new RuntimeException(
137
- String.format("Following plugins failed to output [%s]",
138
- errorPlugins.stream().map(OutputPluginDelegate::getPluginCode).collect(Collectors.joining(", "))
139
- ));
140
- }
141
- report.set(CONFIG_NAME_OUTPUT_TASK_REPORTS, new TaskReports(reports));
142
- return report;
143
- }
144
- };
82
+ return MultiTransactionalPageOutput.open(schema, taskIndex, mapWithPluginDelegate(task, session, Function.identity()));
145
83
  }
146
84
 
147
- private static ConfigDiff buildConfigDiff(List<Future<ConfigDiff>> runPluginTasks) {
85
+ private static ConfigDiff buildConfigDiff(List<OutputPluginDelegate.Transaction> transactions) {
148
86
  final ConfigDiff configDiff = Exec.newConfigDiff();
149
- List<ConfigDiff> configDiffs = new ArrayList<>();
150
- for (Future<ConfigDiff> pluginTask : runPluginTasks) {
87
+ Map<String, ConfigDiff> configDiffs = new HashMap<>();
88
+ for (OutputPluginDelegate.Transaction transaction: transactions) {
151
89
  try {
152
- configDiffs.add(pluginTask.get());
90
+ configDiffs.put(transaction.getTag(), transaction.getResult());
153
91
  } catch (InterruptedException e) {
154
92
  Thread.currentThread().interrupt();
155
93
  throw new RuntimeException(e);
@@ -163,18 +101,29 @@ public class MultiOutputPlugin implements OutputPlugin {
163
101
 
164
102
  private <T> List<T> mapWithPluginDelegate(PluginTask task, ExecSession session, Function<OutputPluginDelegate, T> action) {
165
103
  List<T> result = new ArrayList<>();
166
- for (int pluginIndex = 0; pluginIndex < task.getOutputConfigs().size(); pluginIndex++) {
167
- final ConfigSource config = task.getOutputConfigs().get(pluginIndex);
168
- if (task.getOutputConfigDiffs().isPresent()) {
169
- config.merge(task.getOutputConfigDiffs().get().get(pluginIndex));
170
- }
104
+ for (int i = 0; i < task.getOutputConfigs().size(); i++) {
105
+ final ConfigSource config = task.getOutputConfigs().get(i);
171
106
  final PluginType pluginType = config.get(PluginType.class, "type");
172
107
  final OutputPlugin outputPlugin = session.newPlugin(OutputPlugin.class, pluginType);
108
+
109
+ final String tag = String.format("%s_%d", pluginType.getName(), i);
110
+
111
+ // Merge ConfigDiff if exists
112
+ if (task.getOutputConfigDiffs().isPresent()) {
113
+ final ConfigDiff configDiff = task.getOutputConfigDiffs().get().get(tag);
114
+ if (configDiff != null) {
115
+ config.merge(configDiff);
116
+ } else {
117
+ LOGGER.debug("ConfigDiff for '{}' not found.", tag);
118
+ }
119
+ }
120
+ // Set TaskSource if exists
173
121
  TaskSource taskSource = null;
174
122
  if (task.getTaskSources() != null) {
175
- taskSource = task.getTaskSources().get(pluginIndex);
123
+ taskSource = task.getTaskSources().get(tag);
176
124
  }
177
- result.add(action.apply(new OutputPluginDelegate(pluginIndex, pluginType, outputPlugin, config, taskSource)));
125
+
126
+ result.add(action.apply(new OutputPluginDelegate(tag, outputPlugin, config, taskSource)));
178
127
  }
179
128
  return result;
180
129
  }
@@ -0,0 +1,137 @@
1
+ package org.embulk.output.multi;
2
+
3
+ import org.embulk.config.TaskReport;
4
+ import org.embulk.spi.Buffer;
5
+ import org.embulk.spi.Exec;
6
+ import org.embulk.spi.Page;
7
+ import org.embulk.spi.Schema;
8
+ import org.embulk.spi.TransactionalPageOutput;
9
+ import org.slf4j.Logger;
10
+ import org.slf4j.LoggerFactory;
11
+
12
+ import java.util.ArrayList;
13
+ import java.util.HashMap;
14
+ import java.util.List;
15
+ import java.util.Map;
16
+ import java.util.function.Consumer;
17
+ import java.util.stream.Collectors;
18
+
19
+ public class MultiTransactionalPageOutput implements TransactionalPageOutput {
20
+ private static final Logger LOGGER = LoggerFactory.getLogger(MultiTransactionalPageOutput.class);
21
+ private final int taskIndex;
22
+ private final List<Delegate> delegates;
23
+
24
+ static MultiTransactionalPageOutput open(Schema schema, int taskIndex, List<OutputPluginDelegate> plugins) {
25
+ return new MultiTransactionalPageOutput(
26
+ taskIndex,
27
+ plugins.stream()
28
+ .map(plugin -> new Delegate(plugin, plugin.open(schema, taskIndex)))
29
+ .collect(Collectors.toList())
30
+ );
31
+ }
32
+
33
+ private MultiTransactionalPageOutput(int taskIndex, List<Delegate> delegates) {
34
+ this.taskIndex = taskIndex;
35
+ this.delegates = delegates;
36
+ }
37
+
38
+ @Override
39
+ public void add(Page page) {
40
+ applyToAllPlugins(delegate -> delegate.add(copyPage(page)));
41
+ page.release();
42
+ }
43
+
44
+ @Override
45
+ public void finish() {
46
+ applyToAllPlugins(Delegate::finish);
47
+ }
48
+
49
+ @Override
50
+ public void close() {
51
+ applyToAllPlugins(Delegate::close);
52
+ }
53
+
54
+ @Override
55
+ public void abort() {
56
+ applyToAllPlugins(Delegate::abort);
57
+ }
58
+
59
+ @Override
60
+ public TaskReport commit() {
61
+ final Map<String, TaskReport> reports = new HashMap<>();
62
+ applyToAllPlugins(delegate -> reports.put(delegate.getTag(), delegate.commit()));
63
+ final TaskReport report = Exec.newTaskReport();
64
+ report.set(MultiOutputPlugin.CONFIG_NAME_OUTPUT_TASK_REPORTS, new TaskReports(reports));
65
+ return report;
66
+ }
67
+
68
+ private void applyToAllPlugins(Consumer<Delegate> command) {
69
+ final List<OutputPluginDelegate> errorPlugins = new ArrayList<>();
70
+ for (Delegate delegate : delegates) {
71
+ try {
72
+ command.accept(delegate);
73
+ } catch (Exception e) {
74
+ LOGGER.warn(String.format("Output for %s on index %d failed.", delegate.plugin.getTag(), taskIndex), e);
75
+ errorPlugins.add(delegate.plugin);
76
+ }
77
+ }
78
+
79
+ if (!errorPlugins.isEmpty()) {
80
+ throw new RuntimeException(
81
+ String.format("Following plugins failed to output [%s] on index %d",
82
+ errorPlugins.stream().map(OutputPluginDelegate::getTag).collect(Collectors.joining(", ")),
83
+ taskIndex
84
+ ));
85
+ }
86
+ }
87
+
88
+ private static Page copyPage(Page original) {
89
+ final Buffer originalBuffer = original.buffer();
90
+ final Buffer copiedBuffer = Buffer.wrap(originalBuffer.array(), originalBuffer.offset(), originalBuffer.capacity());
91
+ copiedBuffer.limit(originalBuffer.limit());
92
+
93
+ final Page copiedPage = Page.wrap(copiedBuffer);
94
+ copiedPage.setStringReferences(new ArrayList<>(original.getStringReferences()));
95
+ copiedPage.setValueReferences(new ArrayList<>(original.getValueReferences()));
96
+ return copiedPage;
97
+ }
98
+
99
+ static class Delegate implements TransactionalPageOutput {
100
+ private final OutputPluginDelegate plugin;
101
+ private final TransactionalPageOutput output;
102
+
103
+ private Delegate(OutputPluginDelegate plugin, TransactionalPageOutput output) {
104
+ this.plugin = plugin;
105
+ this.output = output;
106
+ }
107
+
108
+ @Override
109
+ public void add(Page page) {
110
+ output.add(page);
111
+ }
112
+
113
+ @Override
114
+ public void finish() {
115
+ output.finish();
116
+ }
117
+
118
+ @Override
119
+ public void close() {
120
+ output.close();
121
+ }
122
+
123
+ @Override
124
+ public void abort() {
125
+ output.abort();
126
+ }
127
+
128
+ @Override
129
+ public TaskReport commit() {
130
+ return output.commit();
131
+ }
132
+
133
+ String getTag() {
134
+ return plugin.getTag();
135
+ }
136
+ }
137
+ }
@@ -5,7 +5,6 @@ import org.embulk.config.ConfigDiff;
5
5
  import org.embulk.config.ConfigSource;
6
6
  import org.embulk.config.TaskReport;
7
7
  import org.embulk.config.TaskSource;
8
- import org.embulk.plugin.PluginType;
9
8
  import org.embulk.spi.OutputPlugin;
10
9
  import org.embulk.spi.Schema;
11
10
  import org.embulk.spi.TransactionalPageOutput;
@@ -23,105 +22,92 @@ import java.util.concurrent.Future;
23
22
  class OutputPluginDelegate {
24
23
  private static final Logger LOGGER = LoggerFactory.getLogger(OutputPluginDelegate.class);
25
24
  private static final String THREAD_NAME_FORMAT = "multi-output-plugin-%s-%%d";
26
- private final int pluginIndex;
27
- private final PluginType type;
25
+ private final String tag;
28
26
  private final OutputPlugin plugin;
29
27
  private final ConfigSource config;
30
28
  private final TaskSource taskSource;
31
29
  private final ExecutorService executorService;
32
30
 
33
31
  OutputPluginDelegate(
34
- int pluginIndex,
35
- PluginType type,
32
+ String tag,
36
33
  OutputPlugin plugin,
37
34
  ConfigSource config,
38
35
  TaskSource taskSource
39
36
  ) {
40
- this.pluginIndex = pluginIndex;
41
- this.type = type;
37
+ this.tag = tag;
42
38
  this.plugin = plugin;
43
39
  this.config = config;
44
40
  this.taskSource = taskSource;
45
41
  this.executorService = Executors.newSingleThreadExecutor(
46
- new ThreadFactoryBuilder().setNameFormat(String.format(THREAD_NAME_FORMAT, generatePluginCode(type, pluginIndex))).build()
42
+ new ThreadFactoryBuilder().setNameFormat(String.format(THREAD_NAME_FORMAT, tag)).build()
47
43
  );
48
44
  }
49
45
 
50
- Future<ConfigDiff> transaction(Schema schema, int taskCount, AsyncRunControl runControl) {
51
- return executorService.submit(() -> {
46
+ Transaction transaction(Schema schema, int taskCount, AsyncRunControl runControl) {
47
+ return new Transaction(executorService.submit(() -> {
52
48
  try {
53
- LOGGER.debug("Run #transaction for {}", getPluginCode());
54
- return plugin.transaction(config, schema, taskCount, new Control(pluginIndex, runControl));
49
+ LOGGER.debug("Run #transaction for {}", getTag());
50
+ return plugin.transaction(config, schema, taskCount, new Control(runControl));
55
51
  } catch (CancellationException e) {
56
- LOGGER.error("Canceled #transaction for {} by other plugin's error", getPluginCode());
52
+ LOGGER.error("Canceled #transaction for {} by other plugin's error", getTag());
57
53
  throw e;
58
54
  } catch (Exception e) {
59
- LOGGER.error("Transaction for {} failed.", getPluginCode(), e);
55
+ LOGGER.error("Transaction for {} failed.", getTag(), e);
60
56
  runControl.cancel();
61
57
  throw e;
62
58
  } finally {
63
59
  executorService.shutdown();
64
60
  }
65
- });
61
+ }));
66
62
  }
67
63
 
68
- Future<ConfigDiff> resume(Schema schema, int taskCount, AsyncRunControl runControl) {
69
- return executorService.submit(() -> {
64
+ Transaction resume(Schema schema, int taskCount, AsyncRunControl runControl) {
65
+ return new Transaction(executorService.submit(() -> {
70
66
  try {
71
- LOGGER.debug("Run #resume for {}", getPluginCode());
72
- return plugin.resume(taskSource, schema, taskCount, new Control(pluginIndex, runControl));
67
+ LOGGER.debug("Run #resume for {}", getTag());
68
+ return plugin.resume(taskSource, schema, taskCount, new Control(runControl));
73
69
  } catch (CancellationException e) {
74
- LOGGER.error("Canceled #resume for {} by other plugin's error", getPluginCode());
70
+ LOGGER.error("Canceled #resume for {} by other plugin's error", getTag());
75
71
  throw e;
76
72
  } catch (Exception e) {
77
- LOGGER.error("Resume for {} failed.", getPluginCode(), e);
73
+ LOGGER.error("Resume for {} failed.", getTag(), e);
78
74
  runControl.cancel();
79
75
  throw e;
80
76
  } finally {
81
77
  executorService.shutdown();
82
78
  }
83
- });
79
+ }));
84
80
  }
85
81
 
86
82
  void cleanup(Schema schema, int taskCount, List<TaskReport> successTaskReports) {
87
- LOGGER.debug("Run #cleanup for {}", getPluginCode());
83
+ LOGGER.debug("Run #cleanup for {}", getTag());
88
84
  List<TaskReport> successReportsForPlugin = new ArrayList<>();
89
85
  for (TaskReport successTaskReport : successTaskReports) {
90
- final TaskReport report = successTaskReport.get(TaskReports.class, MultiOutputPlugin.CONFIG_NAME_OUTPUT_TASK_REPORTS).get(pluginIndex);
86
+ final TaskReport report = successTaskReport.get(TaskReports.class, MultiOutputPlugin.CONFIG_NAME_OUTPUT_TASK_REPORTS).get(tag);
91
87
  successReportsForPlugin.add(report);
92
88
  }
93
89
  plugin.cleanup(taskSource, schema, taskCount, successReportsForPlugin);
94
90
  }
95
91
 
96
92
  TransactionalPageOutput open(Schema schema, int taskIndex) {
97
- LOGGER.debug("Run #open for {}", getPluginCode());
93
+ LOGGER.debug("Run #open for {}", getTag());
98
94
  return plugin.open(taskSource, schema, taskIndex);
99
95
  }
100
96
 
101
- PluginType getType() {
102
- return type;
97
+ String getTag() {
98
+ return tag;
103
99
  }
104
100
 
105
- String getPluginCode() {
106
- return generatePluginCode(type, pluginIndex);
107
- }
108
-
109
- private static String generatePluginCode(PluginType type, int pluginIndex) {
110
- return String.format("%s-%d", type.getName(), pluginIndex);
111
- }
112
-
113
- private static class Control implements OutputPlugin.Control {
114
- private final int pluginIndex;
101
+ private class Control implements OutputPlugin.Control {
115
102
  private final AsyncRunControl runControl;
116
103
 
117
- Control(int index, AsyncRunControl runControl) {
118
- this.pluginIndex = index;
104
+ Control(AsyncRunControl runControl) {
119
105
  this.runControl = runControl;
120
106
  }
121
107
 
122
108
  @Override
123
109
  public List<TaskReport> run(TaskSource taskSource) {
124
- runControl.addTaskSource(pluginIndex, taskSource);
110
+ runControl.addTaskSource(tag, taskSource);
125
111
  List<TaskReport> reports;
126
112
  try {
127
113
  reports = runControl.waitAndGetResult();
@@ -134,10 +120,26 @@ class OutputPluginDelegate {
134
120
 
135
121
  final List<TaskReport> result = new ArrayList<>();
136
122
  for (TaskReport taskReport : reports) {
137
- final TaskReport report = taskReport.get(TaskReports.class, MultiOutputPlugin.CONFIG_NAME_OUTPUT_TASK_REPORTS).get(pluginIndex);
123
+ final TaskReport report = taskReport.get(TaskReports.class, MultiOutputPlugin.CONFIG_NAME_OUTPUT_TASK_REPORTS).get(tag);
138
124
  result.add(report);
139
125
  }
140
126
  return result;
141
127
  }
142
128
  }
129
+
130
+ class Transaction {
131
+ private final Future<ConfigDiff> future;
132
+
133
+ private Transaction(Future<ConfigDiff> future) {
134
+ this.future = future;
135
+ }
136
+
137
+ String getTag() {
138
+ return OutputPluginDelegate.this.getTag();
139
+ }
140
+
141
+ ConfigDiff getResult() throws ExecutionException, InterruptedException {
142
+ return future.get();
143
+ }
144
+ }
143
145
  }
@@ -4,22 +4,22 @@ import com.fasterxml.jackson.annotation.JsonCreator;
4
4
  import com.fasterxml.jackson.annotation.JsonProperty;
5
5
  import org.embulk.config.TaskReport;
6
6
 
7
- import java.util.List;
7
+ import java.util.Map;
8
8
 
9
9
  class TaskReports {
10
- private final List<TaskReport> reports;
10
+ private final Map<String, TaskReport> reports;
11
11
 
12
12
  @JsonCreator
13
- TaskReports(@JsonProperty("reports") List<TaskReport> reports) {
13
+ TaskReports(@JsonProperty("reports") Map<String, TaskReport> reports) {
14
14
  this.reports = reports;
15
15
  }
16
16
 
17
17
  @JsonProperty("reports")
18
- List<TaskReport> getReports() {
18
+ Map<String, TaskReport> getReports() {
19
19
  return reports;
20
20
  }
21
21
 
22
- TaskReport get(int index) {
23
- return reports.get(index);
22
+ TaskReport get(String tag) {
23
+ return reports.get(tag);
24
24
  }
25
25
  }
@@ -24,10 +24,10 @@ import static org.embulk.test.Utils.configFromResource;
24
24
  import static org.embulk.test.Utils.record;
25
25
  import static org.junit.jupiter.api.Assertions.assertEquals;
26
26
  import static org.junit.jupiter.api.Assertions.assertNull;
27
- import static org.junit.jupiter.api.Assertions.assertTrue;
28
27
 
29
28
  @EmbulkTest(MultiOutputPlugin.class)
30
29
  class TestMultiOutputPlugin extends EmbulkPluginTest {
30
+
31
31
  @Test
32
32
  void testMultipleOutputWorking() throws IOException {
33
33
  final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
@@ -115,7 +115,7 @@ class TestMultiOutputPlugin extends EmbulkPluginTest {
115
115
  }
116
116
 
117
117
  @Test
118
- void testAnOutputFailedFailedAfterOpen() throws IOException {
118
+ void testAnOutputFailedAfterOpen() throws IOException {
119
119
  final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
120
120
  final ConfigSource outConfig = configFromResource("yaml/out_failed_output_after_open.yml");
121
121
  final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -138,7 +138,7 @@ class TestMultiOutputPlugin extends EmbulkPluginTest {
138
138
  }
139
139
 
140
140
  @Test
141
- void testAnOutputFailedFailedBeforeOpen() throws IOException {
141
+ void testAnOutputFailedBeforeOpen() throws IOException {
142
142
  final ConfigSource inConfig = configFromResource("yaml/in_base.yml");
143
143
  final ConfigSource outConfig = configFromResource("yaml/out_failed_output_before_open.yml");
144
144
  final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-multi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shinichi ISHIMURA
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-07 00:00:00.000000000 Z
11
+ date: 2021-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -50,7 +50,7 @@ files:
50
50
  - LICENSE
51
51
  - README.md
52
52
  - build.gradle
53
- - classpath/embulk-output-multi-0.2.1.jar
53
+ - classpath/embulk-output-multi-0.5.0.jar
54
54
  - gradle.properties
55
55
  - gradle/dependency-locks/compileClasspath.lockfile
56
56
  - gradle/dependency-locks/testCompileClasspath.lockfile
@@ -62,10 +62,9 @@ files:
62
62
  - settings.gradle
63
63
  - src/main/java/org/embulk/output/multi/AsyncRunControl.java
64
64
  - src/main/java/org/embulk/output/multi/MultiOutputPlugin.java
65
+ - src/main/java/org/embulk/output/multi/MultiTransactionalPageOutput.java
65
66
  - src/main/java/org/embulk/output/multi/OutputPluginDelegate.java
66
- - src/main/java/org/embulk/output/multi/PluginExecutionException.java
67
67
  - src/main/java/org/embulk/output/multi/TaskReports.java
68
- - src/main/java/org/embulk/output/multi/TransactionalPageOutputDelegate.java
69
68
  - src/test/java/org/embulk/output/multi/TestMultiOutputPlugin.java
70
69
  - src/test/resources/yaml/in_base.yml
71
70
  - src/test/resources/yaml/out_base.yml
@@ -1,15 +0,0 @@
1
- package org.embulk.output.multi;
2
-
3
- class PluginExecutionException extends RuntimeException {
4
-
5
- private final OutputPluginDelegate plugin;
6
-
7
- PluginExecutionException(OutputPluginDelegate plugin, Throwable cause) {
8
- super(cause);
9
- this.plugin = plugin;
10
- }
11
-
12
- OutputPluginDelegate getPlugin() {
13
- return plugin;
14
- }
15
- }
@@ -1,92 +0,0 @@
1
- package org.embulk.output.multi;
2
-
3
- import com.google.common.util.concurrent.ThreadFactoryBuilder;
4
- import org.embulk.config.TaskReport;
5
- import org.embulk.spi.Page;
6
- import org.embulk.spi.TransactionalPageOutput;
7
-
8
- import java.util.concurrent.BlockingQueue;
9
- import java.util.concurrent.Callable;
10
- import java.util.concurrent.ExecutionException;
11
- import java.util.concurrent.ExecutorService;
12
- import java.util.concurrent.Executors;
13
- import java.util.concurrent.Future;
14
- import java.util.concurrent.LinkedBlockingQueue;
15
- import java.util.function.Supplier;
16
-
17
- class TransactionalPageOutputDelegate {
18
- private static final String THREAD_NAME_FORMAT = "multi-page-output-%s-%d";
19
- private final OutputPluginDelegate source;
20
- private final TransactionalPageOutput delegate;
21
- private final BlockingQueue<Supplier<Object>> taskQueue;
22
- private final ExecutorService executorService;
23
- private final Future<TaskReport> result;
24
-
25
- TransactionalPageOutputDelegate(
26
- int taskIndex,
27
- OutputPluginDelegate source,
28
- TransactionalPageOutput delegate
29
- ) {
30
- this.source = source;
31
- this.delegate = delegate;
32
- this.taskQueue = new LinkedBlockingQueue<>();
33
- this.executorService = Executors.newSingleThreadExecutor(
34
- new ThreadFactoryBuilder().setNameFormat(String.format(THREAD_NAME_FORMAT, source.getPluginCode(), taskIndex)).build()
35
- );
36
- this.result = executorService.submit(new Worker());
37
- }
38
-
39
- void add(Page page) {
40
- taskQueue.add(() -> {
41
- delegate.add(page);
42
- return null;
43
- });
44
- }
45
-
46
- void finish() {
47
- taskQueue.add(() -> {
48
- delegate.finish();
49
- return null;
50
- });
51
- }
52
-
53
- void close() {
54
- taskQueue.add(() -> {
55
- delegate.close();
56
- return null;
57
- });
58
- }
59
-
60
- void abort() {
61
- taskQueue.add(() -> {
62
- delegate.abort();
63
- return null;
64
- });
65
- }
66
-
67
- TaskReport commit() {
68
- taskQueue.add(delegate::commit);
69
- try {
70
- return result.get();
71
- } catch (InterruptedException e) {
72
- Thread.currentThread().interrupt();
73
- throw new RuntimeException(e);
74
- } catch (ExecutionException e) {
75
- throw new PluginExecutionException(source, e.getCause());
76
- } finally {
77
- executorService.shutdown();
78
- }
79
- }
80
-
81
- private class Worker implements Callable<TaskReport> {
82
- @Override
83
- public TaskReport call() throws InterruptedException {
84
- while (true) {
85
- final Object result = taskQueue.take().get();
86
- if (result instanceof TaskReport) {
87
- return (TaskReport) result;
88
- }
89
- }
90
- }
91
- }
92
- }