groonga-delta 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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