groonga-delta 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Groonga delta
2
+
3
+ ## Description
4
+
5
+ Groonga delta provides delta based data import tools. They can import data to Groonga from other systems such as local file system, MySQL and PostgreSQL.
6
+
7
+ ## Usage
8
+
9
+ ### Import deltas from other systems
10
+
11
+ Create configuration file: TODO
12
+
13
+ Run `groonga-delta-import` with the created configuration file:
14
+
15
+ ```bash
16
+ docker run \
17
+ --rm \
18
+ --volume /var/lib/groonga-delta:/var/lib/groonga-delta:z \
19
+ ghcr.io/groonga/groonga-delta:latest \
20
+ groonga-delta-import \
21
+ --server \
22
+ --dir=/var/lib/groonga-delta/import
23
+ ```
24
+
25
+ ### Apply imported deltas
26
+
27
+ Create configuration file: TODO
28
+
29
+ Run `groonga-delta-apply` with the created configuration file:
30
+
31
+ ```bash
32
+ docker run \
33
+ --rm \
34
+ --volume /var/lib/groonga-delta:/var/lib/groonga-delta:z \
35
+ ghcr.io/groonga/groonga-delta:latest \
36
+ groonga-delta-apply \
37
+ --server \
38
+ --dir=/var/lib/groonga-delta/apply
39
+ ```
40
+
41
+ ## License
42
+
43
+ GPLv3 or later. See `LICENSE.txt` for details.
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # -*- ruby -*-
2
+ #
3
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require "rubygems"
19
+ require "bundler/gem_helper"
20
+
21
+ base_dir = File.join(__dir__)
22
+
23
+ helper = Bundler::GemHelper.new(base_dir)
24
+ def helper.version_tag
25
+ version
26
+ end
27
+
28
+ helper.install
29
+ spec = helper.gemspec
30
+
31
+ desc "Run tests"
32
+ task :test do
33
+ ruby("test/run.rb")
34
+ end
35
+
36
+ task default: :test
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require_relative "../lib/groonga-delta"
19
+
20
+ apply_command = GroongaDelta::ApplyCommand.new
21
+ exit(apply_command.run(ARGV))
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require_relative "../lib/groonga-delta"
19
+
20
+ import_command = GroongaDelta::ImportCommand.new
21
+ exit(import_command.run(ARGV))
data/doc/text/news.md ADDED
@@ -0,0 +1,5 @@
1
+ # NEWS
2
+
3
+ ## 1.0.0 - 2022-03-07
4
+
5
+ Initial release.
@@ -0,0 +1,54 @@
1
+ # -*- ruby -*-
2
+ #
3
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ clean_white_space = lambda do |entry|
19
+ entry.gsub(/(\A\n+|\n+\z)/, '') + "\n"
20
+ end
21
+
22
+ require_relative "lib/groonga-delta/version"
23
+
24
+ Gem::Specification.new do |spec|
25
+ spec.name = "groonga-delta"
26
+ spec.version = GroongaDelta::VERSION
27
+ spec.homepage = "https://github.com/groonga/groonga-delta"
28
+ spec.authors = ["Sutou Kouhei"]
29
+ spec.email = ["kou@clear-code.com"]
30
+
31
+ readme = File.read("README.md")
32
+ readme.force_encoding("UTF-8")
33
+ entries = readme.split(/^\#\#\s(.*)$/)
34
+ clean_white_space.call(entries[entries.index("Description") + 1])
35
+ description = clean_white_space.call(entries[entries.index("Description") + 1])
36
+ spec.summary, spec.description, = description.split(/\n\n+/, 3)
37
+ spec.license = "GPL-3.0+"
38
+ spec.files = [
39
+ "#{spec.name}.gemspec",
40
+ "Gemfile",
41
+ "LICENSE.txt",
42
+ "README.md",
43
+ "Rakefile",
44
+ "doc/text/news.md",
45
+ ]
46
+ spec.files += Dir.glob("lib/**/*.rb")
47
+ Dir.chdir("bin") do
48
+ spec.executables = Dir.glob("*")
49
+ end
50
+
51
+ spec.add_runtime_dependency("groonga-client")
52
+ spec.add_runtime_dependency("groonga-command-parser")
53
+ spec.add_runtime_dependency("red-parquet")
54
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require_relative "command"
17
+ require_relative "apply-config"
18
+ require_relative "apply-status"
19
+
20
+ require_relative "local-delta"
21
+
22
+ module GroongaDelta
23
+ class ApplyCommand < Command
24
+ private
25
+ def prepare
26
+ @config = ApplyConfig.new(@dir)
27
+ @status = ApplyStatus.new(@dir)
28
+ @deltas = []
29
+ @deltas << LocalDelta.new(@config, @status)
30
+ end
31
+
32
+ def process
33
+ @deltas.each do |delta|
34
+ delta.apply
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,78 @@
1
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require_relative "config"
17
+
18
+ module GroongaDelta
19
+ class ApplyConfig < Config
20
+ def initialize(dir)
21
+ super("groonga-delta-apply", dir)
22
+ validate_on_error(on_error)
23
+ end
24
+
25
+ def on_error
26
+ @data["on_error"] || "error"
27
+ end
28
+
29
+ def groonga
30
+ Groonga.new(@dir, @data["groonga"] || {})
31
+ end
32
+
33
+ def local
34
+ Local.new(@dir, @data["local"] || {})
35
+ end
36
+
37
+ private
38
+ def validate_on_error(on_error)
39
+ case on_error
40
+ when "ignore"
41
+ when "warning"
42
+ when "error"
43
+ else
44
+ message = "on_error must be ignore, warning or error: " +
45
+ on_error.inspect
46
+ raise ConfigError, message
47
+ end
48
+ end
49
+
50
+ class Groonga
51
+ def initialize(dir, data)
52
+ @dir = dir
53
+ @data = data
54
+ end
55
+
56
+ def url
57
+ @data["url"] || "http://127.0.0.1:10041"
58
+ end
59
+
60
+ def read_timeout
61
+ @data["read_timeout"] || ::Groonga::Client::Default::READ_TIMEOUT
62
+ end
63
+ end
64
+
65
+ class Local
66
+ include Config::PathResolvable
67
+
68
+ def initialize(dir, data)
69
+ @dir = dir
70
+ @data = data
71
+ end
72
+
73
+ def delta_dir
74
+ resolve_path(@data["delta_dir"] || "delta")
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (C) 2021 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require_relative "status"
17
+
18
+ module GroongaDelta
19
+ class ApplyStatus < Status
20
+ def start_time
21
+ self["start_time"]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,75 @@
1
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "optparse"
17
+
18
+ module GroongaDelta
19
+ class Command
20
+ def initialize
21
+ @dir = "."
22
+ @server = false
23
+ @config = nil
24
+ end
25
+
26
+ def run(args)
27
+ catch do |tag|
28
+ parse_args(args, tag)
29
+ begin
30
+ prepare
31
+ loop do
32
+ process
33
+ break unless @server
34
+ sleep(@config.polling_interval)
35
+ end
36
+ true
37
+ rescue => error
38
+ @config.logger.error(error) if @config
39
+ raise
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+ def parse_args(args, tag)
46
+ parser = OptionParser.new
47
+ parser.on("--dir=DIR",
48
+ "Use DIR as directory that has configuration files",
49
+ "(#{@dir})") do |dir|
50
+ @dir = dir
51
+ end
52
+ parser.on("--server",
53
+ "Run as a server") do
54
+ @server = true
55
+ end
56
+ parser.on("--version",
57
+ "Show version and exit") do
58
+ puts(VERSION)
59
+ throw(tag, true)
60
+ end
61
+ parser.on("--help",
62
+ "Show this message and exit") do
63
+ puts(parser.help)
64
+ throw(tag, true)
65
+ end
66
+ begin
67
+ parser.parse!(args.dup)
68
+ rescue OptionParser::InvalidOption => error
69
+ puts(error.message)
70
+ puts(parser.help)
71
+ throw(tag, false)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,99 @@
1
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "fileutils"
17
+ require "logger"
18
+ require "yaml"
19
+
20
+ require_relative "ltsv-log-formatter"
21
+
22
+ class Logger::LogDevice
23
+ private
24
+ def add_log_header(file)
25
+ # Disable adding log header
26
+ end
27
+ end
28
+
29
+ module GroongaDelta
30
+ class Config
31
+ module PathResolvable
32
+ private
33
+ def resolve_path(path)
34
+ File.expand_path(path, @dir)
35
+ end
36
+ end
37
+
38
+ include PathResolvable
39
+
40
+ def initialize(name, dir)
41
+ @name = name
42
+ @dir = dir
43
+ @path = File.join(@dir, "config.yaml")
44
+ if File.exist?(@path)
45
+ @data = YAML.load(File.read(@path))
46
+ else
47
+ @data = {}
48
+ end
49
+ @secret_path = File.join(@dir, "secret.yaml")
50
+ if File.exist?(@secret_path)
51
+ @secret_data = YAML.load(File.read(@secret_path))
52
+ else
53
+ @secret_data = {}
54
+ end
55
+ end
56
+
57
+ def logger
58
+ @logger ||= create_logger
59
+ end
60
+
61
+ def log_path
62
+ resolve_path(File.join(@data["log_dir"] || "log",
63
+ "#{@name}.log"))
64
+ end
65
+
66
+ def log_age
67
+ @data["log_age"] || 7
68
+ end
69
+
70
+ def log_max_size
71
+ @data["log_max_size"] || (1024 * 1024)
72
+ end
73
+
74
+ def log_period_suffix
75
+ @data["log_period_suffix"] || "%Y-%m-%d"
76
+ end
77
+
78
+ def log_level
79
+ @data["log_level"] || "info"
80
+ end
81
+
82
+ def polling_interval
83
+ Float(@data["polling_interval"] || "60")
84
+ end
85
+
86
+ private
87
+ def create_logger
88
+ path = log_path
89
+ FileUtils.mkdir_p(File.dirname(path))
90
+ Logger.new(path,
91
+ log_age,
92
+ log_max_size,
93
+ formatter: LTSVLogFormatter.new,
94
+ level: log_level,
95
+ progname: @name,
96
+ shift_period_suffix: log_period_suffix)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright (C) 2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module GroongaDelta
17
+ class Error < StandardError
18
+ end
19
+
20
+ class ConfigError < Error
21
+ end
22
+
23
+ class ExecutionError < Error
24
+ end
25
+
26
+ class ProcessError < Error
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # Copyright (C) 2021-2022 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require_relative "command"
17
+ require_relative "import-config"
18
+ require_relative "import-status"
19
+
20
+ module GroongaDelta
21
+ class ImportCommand < Command
22
+ private
23
+ def prepare
24
+ @config = ImportConfig.new(@dir)
25
+ @status = ImportStatus.new(@dir)
26
+ @sources = []
27
+ if @config.local
28
+ require_relative "local-source"
29
+ @sources << LocalSource.new(@config, @status)
30
+ end
31
+ if @config.mysql
32
+ require_relative "mysql-source"
33
+ @sources << MySQLSource.new(@config, @status)
34
+ end
35
+ end
36
+
37
+ def process
38
+ @sources.each do |source|
39
+ source.import
40
+ end
41
+ end
42
+ end
43
+ end