groonga-query-log 1.7.7 → 1.7.9

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -4
  3. data/doc/text/check-crash.md +152 -0
  4. data/doc/text/news.md +29 -0
  5. data/groonga-query-log.gemspec +4 -2
  6. data/lib/groonga-query-log/command/check-crash.rb +233 -74
  7. data/lib/groonga-query-log/command/show-running-queries.rb +1 -1
  8. data/lib/groonga-query-log/server-verifier.rb +11 -3
  9. data/lib/groonga-query-log/version.rb +2 -2
  10. data/test/command/test-check-crash.rb +584 -0
  11. data/test/command/test-format-regression-test-logs.rb +4 -4
  12. data/test/fixtures/check-crash/process/crash.log +29 -0
  13. data/test/fixtures/check-crash/process/leak.log +27 -0
  14. data/test/fixtures/check-crash/process/normal.log +27 -0
  15. data/test/fixtures/check-crash/query/column_create/flushed/only-opened.log +8 -0
  16. data/test/fixtures/check-crash/query/column_create/flushed/recursive-yes.log +8 -0
  17. data/test/fixtures/check-crash/query/column_create/flushed/target-name-recursive-dependent.log +8 -0
  18. data/test/fixtures/check-crash/query/column_create/unfinished.log +2 -0
  19. data/test/fixtures/check-crash/query/column_create/unflushed/no-flush.log +3 -0
  20. data/test/fixtures/check-crash/query/column_create/unflushed/only-opened.log +7 -0
  21. data/test/fixtures/check-crash/query/column_create/unflushed/recursive-no.log +11 -0
  22. data/test/fixtures/check-crash/query/column_create/unflushed/target-name-recursive-no.log +7 -0
  23. data/test/fixtures/check-crash/query/column_create/unflushed/target-name-recursive-yes.log +7 -0
  24. data/test/fixtures/check-crash/query/delete/flushed/only-opened.log +9 -0
  25. data/test/fixtures/check-crash/query/delete/flushed/recursive-yes.log +8 -0
  26. data/test/fixtures/check-crash/query/delete/flushed/target-name-recursive-dependent.log +8 -0
  27. data/test/fixtures/check-crash/query/delete/unfinished.log +3 -0
  28. data/test/fixtures/check-crash/query/delete/unflushed/no-flush.log +4 -0
  29. data/test/fixtures/check-crash/query/delete/unflushed/only-opened.log +8 -0
  30. data/test/fixtures/check-crash/query/delete/unflushed/recursive-no.log +12 -0
  31. data/test/fixtures/check-crash/query/delete/unflushed/target-name-recursive-no.log +8 -0
  32. data/test/fixtures/check-crash/query/delete/unflushed/target-name-recursive-yes.log +8 -0
  33. data/test/fixtures/check-crash/query/load/flushed/columns-only-opened.log +9 -0
  34. data/test/fixtures/check-crash/query/load/flushed/columns-target-name-recursive-dependent.log +9 -0
  35. data/test/fixtures/check-crash/query/load/flushed/only-opened.log +9 -0
  36. data/test/fixtures/check-crash/query/load/flushed/recursive-yes.log +8 -0
  37. data/test/fixtures/check-crash/query/load/flushed/target-name-recursive-dependent.log +8 -0
  38. data/test/fixtures/check-crash/query/load/unfinished.log +3 -0
  39. data/test/fixtures/check-crash/query/load/unflushed/columns-only-opened.log +8 -0
  40. data/test/fixtures/check-crash/query/load/unflushed/columns-target-name-recursive-dependent.log +8 -0
  41. data/test/fixtures/check-crash/query/load/unflushed/no-flush.log +4 -0
  42. data/test/fixtures/check-crash/query/load/unflushed/only-opened.log +8 -0
  43. data/test/fixtures/check-crash/query/load/unflushed/recursive-no.log +12 -0
  44. data/test/fixtures/check-crash/query/load/unflushed/target-name-recursive-no.log +8 -0
  45. data/test/fixtures/check-crash/query/load/unflushed/target-name-recursive-yes.log +8 -0
  46. data/test/fixtures/check-crash/query/select/flushed/only-opened.log +12 -0
  47. data/test/fixtures/check-crash/query/select/flushed/recursive-yes.log +11 -0
  48. data/test/fixtures/check-crash/query/select/flushed/target-name-recursive-dependent.log +11 -0
  49. data/test/fixtures/check-crash/query/select/unfinished.log +5 -0
  50. data/test/fixtures/check-crash/query/select/unflushed/no-flush.log +6 -0
  51. data/test/fixtures/check-crash/query/select/unflushed/no-load.log +5 -0
  52. data/test/fixtures/check-crash/query/select/unflushed/only-opened.log +10 -0
  53. data/test/fixtures/check-crash/query/select/unflushed/recursive-no.log +14 -0
  54. data/test/fixtures/check-crash/query/select/unflushed/target-name-recursive-no.log +10 -0
  55. data/test/fixtures/check-crash/query/select/unflushed/target-name-recursive-yes.log +10 -0
  56. data/test/fixtures/check-crash/query/table_create/flushed/only-opened.log +8 -0
  57. data/test/fixtures/check-crash/query/table_create/flushed/recursive-yes.log +7 -0
  58. data/test/fixtures/check-crash/query/table_create/flushed/target-name-recursive-dependent.log +7 -0
  59. data/test/fixtures/check-crash/query/table_create/unflushed/no-flush.log +4 -0
  60. data/test/fixtures/check-crash/query/table_create/unflushed/only-opened.log +7 -0
  61. data/test/fixtures/check-crash/query/table_create/unflushed/recursive-no.log +11 -0
  62. data/test/fixtures/check-crash/query/table_create/unflushed/target-name-recursive-no.log +7 -0
  63. data/test/fixtures/check-crash/query/table_create/unflushed/target-name-recursive-yes.log +7 -0
  64. data/test/fixtures/check-crash/query/truncate/flushed/only-opened.log +8 -0
  65. data/test/fixtures/check-crash/query/truncate/flushed/recursive-yes.log +7 -0
  66. data/test/fixtures/check-crash/query/truncate/flushed/target-name-recursive-dependent.log +7 -0
  67. data/test/fixtures/check-crash/query/truncate/unflushed/no-flush.log +3 -0
  68. data/test/fixtures/check-crash/query/truncate/unflushed/only-opened.log +7 -0
  69. data/test/fixtures/check-crash/query/truncate/unflushed/recursive-no.log +11 -0
  70. data/test/fixtures/check-crash/query/truncate/unflushed/target-name-recursive-no.log +7 -0
  71. data/test/fixtures/check-crash/query/truncate/unflushed/target-name-recursive-yes.log +7 -0
  72. data/test/fixtures/reporter/json-stream.expected +1 -1
  73. data/test/fixtures/reporter/json.expected +1 -1
  74. data/test/helper.rb +1 -2
  75. metadata +160 -11
  76. data/test/fixtures/run-regression-test/results/query.log.log +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af3ac522ec27ea395b44209b8ab56521de14b828d67bf4b8d4dad52bb0df2d1c
4
- data.tar.gz: c570a4a49aad267b71047d992e0593afac1290135711a7f45b8114ded4232dbd
3
+ metadata.gz: d37a2e35f6eb1b0002f81d5d3e5e3f1cbb3115816da95d0d2fba95e40d4602ac
4
+ data.tar.gz: f790ad37a5cb6748181588534a8ce6fc06979921e0c5eb68e016c56b2df8f906
5
5
  SHA512:
6
- metadata.gz: 7f0dc1247dc380bd50f8b249fcc7976a800bb67de75f10e4cd600741508f4236d89aa750277043336dc4abd561641804cf53056373338ef9ae2dd2b0f9cc5c0e
7
- data.tar.gz: ddcce99bb10e8b8876fbbce95d9c0c49f5d1b8ca7e6ce8beb4a9178624ab169475ce49173c33f1e1a5796909ae20c1fa1976126033c944757859f18e9a05689f
6
+ metadata.gz: 61c8927fe0bc65eeeb763694af65e2988047264dd1f05a3daf6b2c7fa702f7793e1a9466e5c7764a9f2f48f804a5927093fd62e09414f7ed88184bcee659dbf7
7
+ data.tar.gz: a7aee9d119a6e1569320bf3e4b2abf2b78e8eccb69c694fcc3850ac37fb3848d8fc01e62c2bbd8e845bb2435f13e7e10c2104f1b01bc2275095ee9f9a8ae134d
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # README
2
2
 
3
- [![Build Status](https://travis-ci.org/groonga/groonga-query-log.png?branch=master)](https://travis-ci.org/groonga/groonga-query-log)
3
+ [![Test](https://github.com/groonga/groonga-query-log/actions/workflows/test.yml/badge.svg)](https://github.com/groonga/groonga-query-log/actions/workflows/test.yml)
4
4
 
5
5
  ## Name
6
6
 
@@ -24,6 +24,10 @@ by using groonga-query-log as a tool.
24
24
 
25
25
  TODO...
26
26
 
27
+ ### groonga-query-log-check-crash
28
+
29
+ * [doc/text/check-crash.md](doc/text/check-crash.md)
30
+
27
31
  ### groonga-query-log-detect-memory-leak
28
32
 
29
33
  TODO...
@@ -54,10 +58,9 @@ TODO...
54
58
  * [groonga-command-parser](http://rubygems.org/gems/groonga-command-parser)
55
59
  * [groonga-client](http://rubygems.org/gems/groonga-client)
56
60
 
57
- ## Mailing list
61
+ ## Community
58
62
 
59
- * English: [groonga-talk@lists.sourceforge.net](https://lists.sourceforge.net/lists/listinfo/groonga-talk)
60
- * Japanese: [groonga-dev@lists.sourceforge.jp](http://lists.sourceforge.jp/mailman/listinfo/groonga-dev)
63
+ See [the community documentation](https://groonga.org/docs/community.html) for details.
61
64
 
62
65
  ## Thanks
63
66
 
@@ -0,0 +1,152 @@
1
+ # `groonga-query-log-check-crash`
2
+
3
+ `groonga-query-log-check-crash` is a tool that supports recovery when Groonga crashes.
4
+
5
+ If Groonga crashes while updating a database, the database may become corrupted.
6
+ It shows tables, columns, and indexes that require recovery, so you can use it when checking what needs to be recovered.
7
+
8
+ ## Usage
9
+
10
+ This section describes how to use `groonga-query-log-check-crash`.
11
+
12
+ You need both the process log and the query log to run `groonga-query-log-check-crash`.
13
+ For log output settings and content, see the Groonga documentation: https://groonga.org/docs/reference/log.html
14
+
15
+ Specify the process log and query log. There is no fixed order for specifying them, so specify all logs.
16
+
17
+ Command example:
18
+
19
+ ```bash
20
+ groonga-query-log-check-crash \
21
+ path/to/process-log-2025-* \
22
+ path/to/query-log-2025-* \
23
+ path/to/process-log-2026-* \
24
+ path/to/query-log-2026-*
25
+ ```
26
+
27
+ ### Options
28
+
29
+ #### `--command-format`
30
+
31
+ This script displays any Groonga commands that terminated abnormally.
32
+ Specify the format of that command.
33
+
34
+ You can specify `command` or `uri`, and output will be as follows.
35
+
36
+ `--command-format=command`:
37
+
38
+ ```
39
+ ===
40
+ [unflushed] 2000-01-01T00:00:01+09:00:
41
+ load \
42
+ --table "Data"
43
+ ===
44
+ ```
45
+
46
+ `--command-format=uri`:
47
+
48
+ ```
49
+ ===
50
+ [unflushed] 2000-01-01T00:00:01+09:00: /d/load?table=Data
51
+ ===
52
+ ```
53
+
54
+ The default is `command`.
55
+ When using it as HTTP server, specifying `uri` makes it easier to use when re-running.
56
+
57
+ #### `--pretty-print`
58
+
59
+ This is only available when `command` is specified in `--command-format`.
60
+
61
+ By default, `--pretty-print` is enabled. Disabling it will output Groonga commands on a single line.
62
+
63
+ `--no-pretty-print`:
64
+
65
+ ```
66
+ [unflushed] 2000-01-01T00:00:01+09:00: load --table "Data"
67
+ ```
68
+
69
+ #### `--output-level`
70
+
71
+ You can specify `info` or `debug`. The default value is `info`.
72
+ Please use `info` as the default. `debug` provides more detailed output for developers.
73
+
74
+ ## Reading the Output
75
+
76
+ ### Summary
77
+
78
+ The `Summary` includes the following items.
79
+
80
+ * `crashed`
81
+ * In the case of `yes`, Groonga has crashed.
82
+ * Please report it with the logs.
83
+ * `unflushed`
84
+ * In the case of `yes`, data in memory (non-persistent data) may not have been written to disk (persistent data).
85
+ * Please re-run the target command.
86
+ * `unfinished`
87
+ * In the case of `yes`, the target table, column, or index may be broken.
88
+ * Please rebuild the target table, column, or index.
89
+ * `leak`
90
+ * In the case of `yes`, Groonga is leaking memory.
91
+ * Please report it with the logs.
92
+
93
+ Example:
94
+
95
+ ```
96
+ Summary:
97
+ crashed:yes, unflushed:yes, unfinished:no, leak:no
98
+ ```
99
+
100
+ ### Unflushed
101
+
102
+ If the target command exists, the following output is displayed.
103
+
104
+ ```
105
+ !!!
106
+ !!! [unflushed] Recovery information
107
+ !!!
108
+ There may be commands that were not flushed between 2000-01-01T00:00:00+09:00 and 2000-01-01T12:00:00+09:00.
109
+ These commands may not have been written to the database files, so please re-run them.
110
+ ===
111
+ [unflushed] 2000-01-01T00:00:01+09:00: load --table "Data"
112
+ ===
113
+
114
+ Summary:
115
+ crashed:yes, unflushed:yes, unfinished:no, leak:no
116
+ NG: Please check the display and logs.
117
+ ```
118
+
119
+ Lines beginning with `[unflushed]` are commands that may not have been flushed.
120
+ Since it may not have been flushed, please re-run that command.
121
+
122
+ ### Unfinished
123
+
124
+ If the target command exists, the following output is displayed.
125
+
126
+ ```
127
+ !!!
128
+ !!! [unfinished] Recovery information
129
+ !!!
130
+ Unfinished commands were found due to abnormal termination or other issues.
131
+ It is safer to rebuild the target tables, columns, and indexes because the data may be corrupted.
132
+ ===
133
+ [unfinished] 2000-01-01T00:00:01+09:00: load --table "Data"
134
+ ===
135
+
136
+ Summary:
137
+ crashed:yes, unflushed:no, unfinished:yes, leak:no
138
+ NG: Please check the display and logs.
139
+ ```
140
+
141
+ Lines beginning with `[unfinished]` are commands that did not complete normally due to abnormal termination or other issues.
142
+ In this case, `Data` and any dependent columns or indexes may be broken and need to be rebuilt.
143
+
144
+ ### Others
145
+
146
+ If any output other than `unflushed` or `unfinished` was displayed, there might have been an issue with Groonga.
147
+ Please report the problem to the Groonga team using the links below, and include the logs used for the check if possible.
148
+
149
+ * [Groonga issues](https://github.com/groonga/groonga/issues)
150
+ * [Groonga discussions](https://github.com/groonga/groonga/discussions)
151
+
152
+ This will help with investigating the issue.
data/doc/text/news.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # News
2
2
 
3
+ ## 1.7.9: 2026-01-27
4
+
5
+ ### Improvements
6
+
7
+ * `check-crash`: Improved the output. Made it easier to use for recovery.
8
+
9
+ * Check the [documentation](check-crash.md) as some options have been added.
10
+
11
+ * `check-crash`: Improved the accuracy of unflushed checks.
12
+
13
+ * Improved detection accuracy when no `target_name` is specified for `io_flush`.
14
+
15
+ * Added a `select` command with `--load_table` option specified as the check target for unflushed.
16
+
17
+ * `check-crash`: Showed PID and thread ID
18
+
19
+ * Added support for Ruby 3.4
20
+
21
+ ### Fixes
22
+
23
+ * `show-running-queries`: Fixed running query condition
24
+
25
+ ## 1.7.8: 2024-04-10
26
+
27
+ ### Improvements
28
+
29
+ * `run-regression-test`: Improved processing time for `request_cancel` command verification.
30
+ * Eliminated unnecessary waiting time.
31
+
3
32
  ## 1.7.7: 2024-04-08
4
33
 
5
34
  ### Improvements
@@ -1,6 +1,6 @@
1
1
  # -*- ruby -*-
2
2
  #
3
- # Copyright (C) 2012-2019 Sutou Kouhei <kou@clear-code.com>
3
+ # Copyright (C) 2012-2025 Sutou Kouhei <kou@clear-code.com>
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -52,11 +52,13 @@ Gem::Specification.new do |spec|
52
52
  spec.licenses = ["LGPLv2.1+"]
53
53
  spec.require_paths = ["lib"]
54
54
 
55
+ spec.add_runtime_dependency("base64")
55
56
  spec.add_runtime_dependency("charty")
56
57
  spec.add_runtime_dependency("diff-lcs")
57
- spec.add_runtime_dependency("net-smtp")
58
58
  spec.add_runtime_dependency("groonga-client", ">= 0.6.2")
59
59
  spec.add_runtime_dependency("groonga-log", ">= 0.1.2")
60
+ spec.add_runtime_dependency("net-smtp")
61
+ spec.add_runtime_dependency("ostruct")
60
62
 
61
63
  spec.add_development_dependency("test-unit")
62
64
  spec.add_development_dependency("test-unit-rr")
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2018 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2018-2025 Sutou Kouhei <kou@clear-code.com>
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -34,6 +34,11 @@ module GroongaQueryLog
34
34
  return false
35
35
  end
36
36
 
37
+ if log_paths.empty?
38
+ puts(@option_parser.help)
39
+ return false
40
+ end
41
+
37
42
  begin
38
43
  check(log_paths)
39
44
  rescue Interrupt
@@ -47,11 +52,35 @@ module GroongaQueryLog
47
52
 
48
53
  private
49
54
  def setup_options
50
- @options = {}
55
+ available_command_format = [:command, :uri]
56
+ available_output_levels = [:info, :debug]
57
+ @options = {
58
+ command_format: :command,
59
+ output_level: :info,
60
+ pretty_print: true
61
+ }
51
62
 
52
63
  @option_parser = OptionParser.new do |parser|
53
64
  parser.version = VERSION
54
65
  parser.banner += " LOG1 ..."
66
+ parser.on("--command-format=FORMAT",
67
+ available_command_format,
68
+ "Specify the output format of the Groonga command that had a problem. [#{@options[:command_format]}]",
69
+ "(#{available_command_format.join(", ")})") do |format|
70
+ @options[:command_format] = format
71
+ end
72
+ parser.on("--[no-]pretty-print",
73
+ "Specify to make command output easier to read. [#{@options[:pretty_print]}]",
74
+ "Only available when `--command-format=command` is specified.") do |boolean|
75
+ @options[:pretty_print] = boolean
76
+ end
77
+ parser.on("--output-level=LEVEL",
78
+ available_output_levels,
79
+ "Specify the output level. [#{@options[:output_level]}]",
80
+ "Specifying 'debug' displays detailed information.",
81
+ "(#{available_output_levels.join(", ")})") do |output_level|
82
+ @options[:output_level] = output_level
83
+ end
55
84
  end
56
85
  end
57
86
 
@@ -66,7 +95,7 @@ module GroongaQueryLog
66
95
  end
67
96
 
68
97
  def check(log_paths)
69
- checker = Checker.new(log_paths)
98
+ checker = Checker.new(log_paths, @options)
70
99
  checker.check
71
100
  end
72
101
 
@@ -111,58 +140,62 @@ module GroongaQueryLog
111
140
  end
112
141
 
113
142
  class Checker
114
- def initialize(log_paths)
143
+ def initialize(log_paths, options)
115
144
  split_log_paths(log_paths)
145
+ @options = options
116
146
  end
117
147
 
118
148
  def check
149
+ summary = {
150
+ crashed: false,
151
+ unflushed: false,
152
+ unfinished: false,
153
+ leak: false,
154
+ }
119
155
  processes = ProcessEnumerator.new(@general_log_paths)
120
156
  processes.each do |process|
121
157
  need_query_log_parsing = true
122
158
  if process.successfully_finished?
123
159
  need_query_log_parsing = false
124
- p [:process,
125
- :success,
126
- process.version,
127
- process.start_time.iso8601,
128
- process.end_time.iso8601,
129
- process.pid,
130
- process.start_log_path,
131
- process.end_log_path]
160
+ debug([:process,
161
+ :success,
162
+ process.version,
163
+ process.start_time.iso8601,
164
+ process.end_time.iso8601,
165
+ process.pid,
166
+ process.start_log_path,
167
+ process.end_log_path].inspect)
132
168
  elsif process.crashed?
133
- p [:process,
134
- :crashed,
135
- process.version,
136
- process.start_time.iso8601,
137
- process.end_time.iso8601,
138
- process.pid,
139
- process.start_log_path,
140
- process.end_log_path]
169
+ debug([:process,
170
+ :crashed,
171
+ process.version,
172
+ process.start_time.iso8601,
173
+ process.end_time.iso8601,
174
+ process.pid,
175
+ process.start_log_path,
176
+ process.end_log_path].inspect)
177
+ summary[:crashed] = true
141
178
  else
142
- p [:process,
143
- :unfinished,
144
- process.version,
145
- process.start_time.iso8601,
146
- process.pid,
147
- process.start_log_path]
179
+ debug([:process,
180
+ :unfinished,
181
+ process.version,
182
+ process.start_time.iso8601,
183
+ process.pid,
184
+ process.start_log_path].inspect)
148
185
  end
149
186
 
150
187
  unless process.n_leaks.zero?
151
- p [:leak,
152
- process.version,
153
- process.n_leaks,
154
- process.end_time.iso8601,
155
- process.pid,
156
- process.end_log_path]
188
+ debug([:leak,
189
+ process.version,
190
+ process.n_leaks,
191
+ process.end_time.iso8601,
192
+ process.pid,
193
+ process.end_log_path].inspect)
194
+ summary[:leak] = true
157
195
  end
158
196
 
159
197
  unless process.important_entries.empty?
160
- puts("Important entries:")
161
- process.important_entries.each_with_index do |entry, i|
162
- puts("#{entry.timestamp.iso8601}: " +
163
- "#{entry.log_level}: " +
164
- "#{entry.message}")
165
- end
198
+ output_important_entries_info(process.important_entries)
166
199
  end
167
200
 
168
201
  next unless need_query_log_parsing
@@ -183,20 +216,22 @@ module GroongaQueryLog
183
216
  statistic.start_time < start_time
184
217
  end
185
218
  unless target_parsing_statistics.empty?
186
- puts("Running queries:")
187
- target_parsing_statistics.each do |statistic|
188
- puts("#{statistic.start_time.iso8601}:")
189
- puts(statistic.command.to_command_format(pretty_print: true))
190
- end
219
+ summary[:unfinished] = true
220
+ output_unfinished_info(target_parsing_statistics)
191
221
  end
192
222
  unless @unflushed_statistics.empty?
193
- puts("Unflushed commands in " +
194
- "#{start_time.iso8601}/#{end_time.iso8601}")
195
- @unflushed_statistics.each do |statistic|
196
- puts("#{statistic.start_time.iso8601}: #{statistic.raw_command}")
197
- end
223
+ summary[:unflushed] = true
224
+ output_unflushed_info(start_time, end_time)
198
225
  end
199
226
  end
227
+ info("",
228
+ "Summary:",
229
+ summary.map {|k, v| "#{k}:#{v ? "yes" : "no"}" }.join(", "))
230
+ if summary.value?(true)
231
+ info("NG: Please check the display and logs.")
232
+ else
233
+ info("OK: no problems.")
234
+ end
200
235
  end
201
236
 
202
237
  private
@@ -215,6 +250,21 @@ module GroongaQueryLog
215
250
  end
216
251
  end
217
252
 
253
+ def formated_command(command)
254
+ if @options[:command_format] == :uri
255
+ command.to_uri_format
256
+ else
257
+ (@options[:pretty_print] ? "\n" : "") +
258
+ command.to_command_format(pretty_print: @options[:pretty_print])
259
+ end
260
+ end
261
+
262
+ def with_load?(statistic)
263
+ statistic.operations.any? do |operation|
264
+ operation[:name] == "load"
265
+ end
266
+ end
267
+
218
268
  def check_query_log_statistic(path, statistic)
219
269
  command = statistic.command
220
270
  return if command.nil?
@@ -230,7 +280,7 @@ module GroongaQueryLog
230
280
  @flushed = false
231
281
  @unflushed_statistics << statistic
232
282
  when "io_flush"
233
- check_io_flush(command)
283
+ check_io_flush(statistic)
234
284
  when "database_unmap"
235
285
  @unflushed_statistics.reject! do |statistic|
236
286
  command.name == "load"
@@ -246,33 +296,65 @@ module GroongaQueryLog
246
296
  when "plugin_register", "plugin_unregister"
247
297
  @flushed = false
248
298
  @unflushed_statistics << statistic
299
+ when "select"
300
+ if with_load?(statistic)
301
+ @flushed = false
302
+ @unflushed_statistics << statistic
303
+ end
304
+ end
305
+ end
306
+
307
+ def flushed_objects(statistic)
308
+ objects = {}
309
+ statistic.operations.each do |operation|
310
+ object = operation[:name][/\Aflush\[(.+)\]/, 1]
311
+ objects[object] = true unless object.nil?
312
+ end
313
+ objects
314
+ end
315
+
316
+ def load_check_keys(statistic)
317
+ case statistic.command.command_name
318
+ when "load"
319
+ [:table, :columns]
320
+ when "select"
321
+ [:load_table, :load_columns]
322
+ else
323
+ raise "Unsupported command name"
249
324
  end
250
325
  end
251
326
 
252
- def check_io_flush(io_flush)
253
- # TODO: Improve flushed target detection.
327
+ def flushed_load?(statistic, flushed)
328
+ table_key, columns_key = load_check_keys(statistic)
329
+
330
+ table_name = statistic.command.arguments[table_key]
331
+ return false unless flushed.key?(table_name)
332
+ columns = statistic.command.arguments[columns_key]
333
+ return true unless columns
334
+ columns.split(",").each do |name|
335
+ name.strip!
336
+ next if name == "_key"
337
+ return false unless flushed["#{table_name}.#{name}"]
338
+ end
339
+ true
340
+ end
341
+
342
+ def check_io_flush(io_flush_statistic)
343
+ io_flush = io_flush_statistic.command
254
344
  if io_flush.target_name
255
- if io_flush.recursive?
345
+ if io_flush.recursive_dependent?
256
346
  @unflushed_statistics.reject! do |statistic|
257
347
  case statistic.command.command_name
258
- when "load"
259
- # TODO: Not enough
260
- statistic.command.table == io_flush.target_name
348
+ when "load", "select"
349
+ flushed_load?(statistic, flushed_objects(io_flush_statistic))
261
350
  when "delete"
262
- # TODO: Not enough
263
351
  statistic.command.table == io_flush.target_name
264
352
  when "truncate"
265
- # TODO: Not enough
266
353
  statistic.command.target_name == io_flush.target_name
267
- else
268
- false
269
- end
270
- end
271
- else
272
- @unflushed_statistics.reject! do |statistic|
273
- case statistic.command.command_name
274
- when /_create/
275
- true # TODO: Need io_flush for database
354
+ when "table_create"
355
+ statistic.command.name == io_flush.target_name
356
+ when "column_create"
357
+ "#{statistic.command.table}.#{statistic.command.name}" == io_flush.target_name
276
358
  else
277
359
  false
278
360
  end
@@ -280,24 +362,101 @@ module GroongaQueryLog
280
362
  end
281
363
  else
282
364
  if io_flush.recursive?
283
- @unflushed_statistics.clear
284
- else
365
+ flushed = flushed_objects(io_flush_statistic)
285
366
  @unflushed_statistics.reject! do |statistic|
286
367
  case statistic.command.command_name
287
- when /_create\z/
288
- true # TODO: Need io_flush for the target
289
- when /_remove\z/, /_rename\z/
290
- true
291
- when "plugin_register", "plugin_unregister"
292
- true
368
+ when "load", "select"
369
+ # TODO: Not enough
370
+ flushed_load?(statistic, flushed_objects(io_flush_statistic))
371
+ when "delete"
372
+ # TODO: Not enough
373
+ flushed.key?(statistic.command.table)
374
+ when "truncate"
375
+ # TODO: Not enough
376
+ flushed.key?(statistic.command.target_name)
377
+ when "table_create"
378
+ # TODO: Not enough
379
+ flushed.key?(statistic.command.name)
380
+ when "column_create"
381
+ # TODO: Not enough
382
+ flushed.key?("#{statistic.command.table}.#{statistic.command.name}")
293
383
  else
294
384
  false
295
385
  end
296
386
  end
297
387
  end
388
+ @unflushed_statistics.reject! do |statistic|
389
+ case statistic.command.command_name
390
+ when /_remove\z/, /_rename\z/
391
+ true
392
+ when "plugin_register", "plugin_unregister"
393
+ true
394
+ else
395
+ false
396
+ end
397
+ end
298
398
  end
299
399
  @flushed = @unflushed_statistics.empty?
300
400
  end
401
+
402
+ def debug(*messages)
403
+ return unless @options[:output_level] == :debug
404
+ puts(*messages)
405
+ end
406
+
407
+ def info(*messages)
408
+ puts(*messages)
409
+ end
410
+
411
+ def output_important_entries_info(entries)
412
+ info("",
413
+ "!!!",
414
+ "!!! Important entries",
415
+ "!!!",
416
+ "It contained logs that require checking.",
417
+ "If you need help, please feel free to contact the community: https://groonga.org/docs/community.html",
418
+ "===")
419
+ entries.each do |entry|
420
+ info("#{entry.timestamp.iso8601}: " +
421
+ "#{entry.pid}: " +
422
+ "#{entry.thread_id}: " +
423
+ "#{entry.log_level}: " +
424
+ "#{entry.message}")
425
+ end
426
+ info("===")
427
+ end
428
+
429
+ def output_statistics_info(message, tag, statistics)
430
+ info(message, "===")
431
+ statistics.each do |statistic|
432
+ info("[#{tag}] #{statistic.start_time.iso8601}: #{formated_command(statistic.command)}")
433
+ end
434
+ info("===")
435
+ end
436
+
437
+ def output_unfinished_info(statistics)
438
+ message = <<-MESSAGE
439
+
440
+ !!!
441
+ !!! [unfinished] Recovery information
442
+ !!!
443
+ Unfinished commands were found due to abnormal termination or other issues.
444
+ It is safer to rebuild the target tables, columns, and indexes because the data may be corrupted.
445
+ MESSAGE
446
+ output_statistics_info(message, "unfinished", statistics)
447
+ end
448
+
449
+ def output_unflushed_info(start_time, end_time)
450
+ message = <<-MESSAGE
451
+
452
+ !!!
453
+ !!! [unflushed] Recovery information
454
+ !!!
455
+ There may be commands that were not flushed between #{start_time.iso8601} and #{end_time.iso8601}.
456
+ These commands may not have been written to the database files, so please re-run them.
457
+ MESSAGE
458
+ output_statistics_info(message, "unflushed", @unflushed_statistics)
459
+ end
301
460
  end
302
461
 
303
462
  class ProcessEnumerator
@@ -61,7 +61,7 @@ module GroongaQueryLog
61
61
  parser.parse(input) do |statistic|
62
62
  next if @base_time.nil?
63
63
  next if statistic.start_time < @base_time
64
- if statistic.start_time == @base_time
64
+ if statistic.end_time > @base_time
65
65
  yield(statistic)
66
66
  end
67
67
  throw(tag)