feedx 0.12.2 → 0.12.7
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.
- checksums.yaml +4 -4
- data/.editorconfig +3 -0
- data/.github/workflows/lint.yml +18 -0
- data/.github/workflows/test.yml +48 -0
- data/.gitignore +1 -0
- data/.golangci.yml +4 -0
- data/.rubocop.yml +14 -5
- data/Gemfile +0 -2
- data/Gemfile.lock +60 -50
- data/Makefile +6 -6
- data/README.md +1 -1
- data/compression.go +18 -0
- data/compression_test.go +17 -5
- data/consumer.go +12 -3
- data/consumer_test.go +50 -19
- data/ext/parquet/decoder.go +59 -0
- data/ext/parquet/decoder_test.go +88 -0
- data/ext/parquet/encoder.go +27 -0
- data/ext/parquet/encoder_test.go +70 -0
- data/ext/parquet/go.mod +12 -0
- data/ext/parquet/go.sum +193 -0
- data/ext/parquet/parquet.go +78 -0
- data/ext/parquet/parquet_test.go +28 -0
- data/ext/parquet/testdata/alltypes_plain.parquet +0 -0
- data/feedx.gemspec +6 -6
- data/feedx_ext_test.go +6 -0
- data/feedx_test.go +8 -6
- data/format.go +45 -15
- data/format_test.go +7 -5
- data/go.mod +8 -5
- data/go.sum +95 -32
- data/internal/testdata/testdata.pb.go +176 -77
- data/lib/feedx/cache/memory.rb +1 -0
- data/lib/feedx/format.rb +1 -1
- data/lib/feedx/producer.rb +18 -19
- data/lib/feedx/stream.rb +4 -8
- data/producer_test.go +4 -4
- data/reader_test.go +6 -5
- data/spec/feedx/cache/memory_spec.rb +2 -2
- data/spec/feedx/cache/value_spec.rb +1 -1
- data/spec/feedx/compression/gzip_spec.rb +1 -1
- data/spec/feedx/compression/none_spec.rb +1 -1
- data/spec/feedx/compression_spec.rb +2 -2
- data/spec/feedx/consumer_spec.rb +5 -4
- data/spec/feedx/format/abstract_spec.rb +2 -1
- data/spec/feedx/format/json_spec.rb +6 -6
- data/spec/feedx/format/parquet_spec.rb +1 -1
- data/spec/feedx/format/protobuf_spec.rb +1 -1
- data/spec/feedx/format_spec.rb +2 -2
- data/spec/feedx/producer_spec.rb +10 -9
- data/spec/feedx/stream_spec.rb +30 -18
- data/writer.go +1 -4
- data/writer_test.go +8 -8
- metadata +30 -25
- data/.travis.yml +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a8328c3af95657d103cf62048d13cb5a14cc50466351f2178082612c347a8cf
|
4
|
+
data.tar.gz: f2fdb20a577c88f165f2cbac3dadf217889716b67b9ae20e4941ed9ef396e864
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cf78b2fb0917c7afb33014e6b3e30d045ffca874f4c8463092f25a7b22b268ab9f2cbd03a1dfda9df7a9314fe213d11c332ff965eb7b81436768cb94d80bf83
|
7
|
+
data.tar.gz: 9131ba95738e600ab4e731546b6e876cae77120782e148a6eac6e3ea4b90e398975f15f2af00b325e86f3fc73a79c39c4a6be25e80bc7298881ff636d61d150b
|
data/.editorconfig
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
name: Lint
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- main
|
6
|
+
pull_request:
|
7
|
+
branches:
|
8
|
+
- main
|
9
|
+
jobs:
|
10
|
+
golangci:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- name: Checkout
|
14
|
+
uses: actions/checkout@v2
|
15
|
+
- name: Run lint
|
16
|
+
uses: golangci/golangci-lint-action@v2
|
17
|
+
with:
|
18
|
+
version: latest
|
@@ -0,0 +1,48 @@
|
|
1
|
+
name: Test
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- main
|
6
|
+
pull_request:
|
7
|
+
branches:
|
8
|
+
- main
|
9
|
+
jobs:
|
10
|
+
go:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
go-version: [1.16.x, 1.17.x]
|
15
|
+
steps:
|
16
|
+
- name: Checkout
|
17
|
+
uses: actions/checkout@v2
|
18
|
+
- name: Cache dependencies
|
19
|
+
uses: actions/cache@v2
|
20
|
+
with:
|
21
|
+
path: ~/go/pkg/mod
|
22
|
+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
23
|
+
restore-keys: |
|
24
|
+
${{ runner.os }}-go-
|
25
|
+
- name: Setup Go
|
26
|
+
uses: actions/setup-go@v2
|
27
|
+
with:
|
28
|
+
go-version: ${{ matrix.go-version }}
|
29
|
+
- name: Run tests
|
30
|
+
run: make test
|
31
|
+
ruby:
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
strategy:
|
34
|
+
matrix:
|
35
|
+
ruby-version: ["2.7", "3.0", "3.1"]
|
36
|
+
steps:
|
37
|
+
- name: Install libarrow
|
38
|
+
run: |
|
39
|
+
wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
|
40
|
+
sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
|
41
|
+
sudo apt update
|
42
|
+
sudo apt install -y libarrow-dev libarrow-glib-dev libarrow-dataset-dev libplasma-dev libplasma-glib-dev libgandiva-dev libgandiva-glib-dev libparquet-dev libparquet-glib-dev
|
43
|
+
- uses: actions/checkout@v2
|
44
|
+
- uses: ruby/setup-ruby@v1
|
45
|
+
with:
|
46
|
+
ruby-version: ${{ matrix.ruby-version }}
|
47
|
+
bundler-cache: true
|
48
|
+
- run: bundle exec rake
|
data/.gitignore
CHANGED
data/.golangci.yml
ADDED
data/.rubocop.yml
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-bsm:
|
3
|
+
- default.yml
|
4
|
+
inherit_mode:
|
5
|
+
merge:
|
6
|
+
- Exclude
|
4
7
|
|
5
8
|
AllCops:
|
6
|
-
TargetRubyVersion:
|
9
|
+
TargetRubyVersion: 2.7
|
10
|
+
Naming/MethodParameterName:
|
11
|
+
MinNameLength: 2
|
12
|
+
Naming/MemoizedInstanceVariableName:
|
13
|
+
Enabled: false
|
14
|
+
Naming/FileName:
|
15
|
+
Exclude: [lib/sortable-by.rb]
|
7
16
|
Metrics/ParameterLists:
|
8
|
-
Max:
|
17
|
+
Max: 8
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,70 +1,82 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
feedx (0.12.
|
5
|
-
bfs (>= 0.
|
4
|
+
feedx (0.12.7)
|
5
|
+
bfs (>= 0.8.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ast (2.4.
|
11
|
-
bfs (0.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
ast (2.4.2)
|
11
|
+
bfs (0.9.0)
|
12
|
+
bigdecimal (3.1.1)
|
13
|
+
diff-lcs (1.5.0)
|
14
|
+
extpp (0.1.0)
|
15
|
+
gio2 (3.4.9)
|
16
|
+
gobject-introspection (= 3.4.9)
|
17
|
+
glib2 (3.4.9)
|
17
18
|
native-package-installer (>= 1.0.3)
|
18
19
|
pkg-config (>= 1.3.5)
|
19
|
-
gobject-introspection (3.4.
|
20
|
-
glib2 (= 3.4.
|
21
|
-
google-protobuf (3.
|
22
|
-
native-package-installer (1.
|
23
|
-
parallel (1.
|
24
|
-
parser (
|
20
|
+
gobject-introspection (3.4.9)
|
21
|
+
glib2 (= 3.4.9)
|
22
|
+
google-protobuf (3.19.2)
|
23
|
+
native-package-installer (1.1.1)
|
24
|
+
parallel (1.21.0)
|
25
|
+
parser (3.1.0.0)
|
25
26
|
ast (~> 2.4.1)
|
26
|
-
pbio (0.
|
27
|
+
pbio (0.3.0)
|
27
28
|
google-protobuf
|
28
|
-
pkg-config (1.4.
|
29
|
+
pkg-config (1.4.6)
|
29
30
|
rainbow (3.0.0)
|
30
|
-
rake (13.0.
|
31
|
-
red-arrow (0.
|
31
|
+
rake (13.0.6)
|
32
|
+
red-arrow (3.0.0)
|
33
|
+
bigdecimal (>= 2.0.3)
|
32
34
|
extpp (>= 0.0.7)
|
33
35
|
gio2 (>= 3.3.6)
|
34
36
|
native-package-installer
|
35
37
|
pkg-config
|
36
|
-
red-parquet (0.
|
37
|
-
red-arrow (= 0.
|
38
|
-
regexp_parser (
|
39
|
-
rexml (3.2.
|
40
|
-
rspec (3.
|
41
|
-
rspec-core (~> 3.
|
42
|
-
rspec-expectations (~> 3.
|
43
|
-
rspec-mocks (~> 3.
|
44
|
-
rspec-core (3.
|
45
|
-
rspec-support (~> 3.
|
46
|
-
rspec-expectations (3.
|
38
|
+
red-parquet (3.0.0)
|
39
|
+
red-arrow (= 3.0.0)
|
40
|
+
regexp_parser (2.2.0)
|
41
|
+
rexml (3.2.5)
|
42
|
+
rspec (3.10.0)
|
43
|
+
rspec-core (~> 3.10.0)
|
44
|
+
rspec-expectations (~> 3.10.0)
|
45
|
+
rspec-mocks (~> 3.10.0)
|
46
|
+
rspec-core (3.10.1)
|
47
|
+
rspec-support (~> 3.10.0)
|
48
|
+
rspec-expectations (3.10.1)
|
47
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
-
rspec-support (~> 3.
|
49
|
-
rspec-mocks (3.
|
50
|
+
rspec-support (~> 3.10.0)
|
51
|
+
rspec-mocks (3.10.2)
|
50
52
|
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
-
rspec-support (~> 3.
|
52
|
-
rspec-support (3.
|
53
|
-
rubocop (
|
53
|
+
rspec-support (~> 3.10.0)
|
54
|
+
rspec-support (3.10.3)
|
55
|
+
rubocop (1.24.1)
|
54
56
|
parallel (~> 1.10)
|
55
|
-
parser (>=
|
57
|
+
parser (>= 3.0.0.0)
|
56
58
|
rainbow (>= 2.2.2, < 4.0)
|
57
|
-
regexp_parser (>= 1.
|
59
|
+
regexp_parser (>= 1.8, < 3.0)
|
58
60
|
rexml
|
59
|
-
rubocop-ast (>=
|
61
|
+
rubocop-ast (>= 1.15.1, < 2.0)
|
60
62
|
ruby-progressbar (~> 1.7)
|
61
|
-
unicode-display_width (>= 1.4.0, <
|
62
|
-
rubocop-ast (
|
63
|
-
parser (>=
|
64
|
-
rubocop-
|
65
|
-
rubocop (
|
66
|
-
|
67
|
-
|
63
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
64
|
+
rubocop-ast (1.15.1)
|
65
|
+
parser (>= 3.0.1.1)
|
66
|
+
rubocop-bsm (0.6.0)
|
67
|
+
rubocop (~> 1.0)
|
68
|
+
rubocop-performance
|
69
|
+
rubocop-rake
|
70
|
+
rubocop-rspec
|
71
|
+
rubocop-performance (1.13.1)
|
72
|
+
rubocop (>= 1.7.0, < 2.0)
|
73
|
+
rubocop-ast (>= 0.4.0)
|
74
|
+
rubocop-rake (0.6.0)
|
75
|
+
rubocop (~> 1.0)
|
76
|
+
rubocop-rspec (2.7.0)
|
77
|
+
rubocop (~> 1.19)
|
78
|
+
ruby-progressbar (1.11.0)
|
79
|
+
unicode-display_width (2.1.0)
|
68
80
|
|
69
81
|
PLATFORMS
|
70
82
|
ruby
|
@@ -72,13 +84,11 @@ PLATFORMS
|
|
72
84
|
DEPENDENCIES
|
73
85
|
bundler
|
74
86
|
feedx!
|
75
|
-
google-protobuf (>= 3.7.0.pre.rc2)
|
76
87
|
pbio
|
77
88
|
rake
|
78
|
-
red-parquet
|
89
|
+
red-parquet (>= 3.0, < 4.0)
|
79
90
|
rspec
|
80
|
-
rubocop
|
81
|
-
rubocop-performance
|
91
|
+
rubocop-bsm
|
82
92
|
|
83
93
|
BUNDLED WITH
|
84
|
-
2.
|
94
|
+
2.2.27
|
data/Makefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
default:
|
1
|
+
default: test
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
.common.makefile:
|
4
|
+
curl -fsSL -o $@ https://gitlab.com/bsm/misc/raw/master/make/go/common.makefile
|
5
5
|
|
6
|
-
|
7
|
-
go vet ./...
|
6
|
+
include .common.makefile
|
8
7
|
|
9
8
|
proto: internal/testdata/testdata.pb.go
|
10
9
|
|
11
10
|
%.pb.go: %.proto
|
12
|
-
|
11
|
+
# may need to `go install google.golang.org/protobuf/cmd/protoc-gen-go`
|
12
|
+
protoc -I=. --go_out=paths=source_relative:. $<
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Feedx
|
2
2
|
|
3
|
-
[](https://github.com/bsm/feedx/actions/workflows/test.yml)
|
4
4
|
[](https://opensource.org/licenses/Apache-2.0)
|
5
5
|
|
6
6
|
Feed-based data exchange between services.
|
data/compression.go
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
package feedx
|
2
2
|
|
3
3
|
import (
|
4
|
+
"compress/flate"
|
4
5
|
"compress/gzip"
|
5
6
|
"io"
|
6
7
|
"path"
|
@@ -20,6 +21,8 @@ func DetectCompression(name string) Compression {
|
|
20
21
|
ext := path.Ext(path.Base(name))
|
21
22
|
if ext != "" && ext[0] == '.' && ext[len(ext)-1] == 'z' {
|
22
23
|
return GZipCompression
|
24
|
+
} else if ext == ".flate" {
|
25
|
+
return FlateCompression
|
23
26
|
}
|
24
27
|
}
|
25
28
|
return NoCompression
|
@@ -61,3 +64,18 @@ func (gzipCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
|
|
61
64
|
func (gzipCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
|
62
65
|
return gzip.NewWriter(w), nil
|
63
66
|
}
|
67
|
+
|
68
|
+
// --------------------------------------------------------------------
|
69
|
+
|
70
|
+
// FlateCompression supports flate compression format.
|
71
|
+
var FlateCompression = flateCompression{}
|
72
|
+
|
73
|
+
type flateCompression struct{}
|
74
|
+
|
75
|
+
func (flateCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
|
76
|
+
return flate.NewReader(r), nil
|
77
|
+
}
|
78
|
+
|
79
|
+
func (flateCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
|
80
|
+
return flate.NewWriter(w, flate.BestSpeed)
|
81
|
+
}
|
data/compression_test.go
CHANGED
@@ -4,8 +4,8 @@ import (
|
|
4
4
|
"bytes"
|
5
5
|
|
6
6
|
"github.com/bsm/feedx"
|
7
|
-
. "github.com/
|
8
|
-
. "github.com/
|
7
|
+
. "github.com/bsm/ginkgo"
|
8
|
+
. "github.com/bsm/gomega"
|
9
9
|
)
|
10
10
|
|
11
11
|
var _ = Describe("Compression", func() {
|
@@ -32,7 +32,7 @@ var _ = Describe("Compression", func() {
|
|
32
32
|
Expect(r.Close()).To(Succeed())
|
33
33
|
}
|
34
34
|
|
35
|
-
It("
|
35
|
+
It("detects the format", func() {
|
36
36
|
Expect(feedx.DetectCompression("/path/to/file.json")).To(Equal(feedx.NoCompression))
|
37
37
|
Expect(feedx.DetectCompression("/path/to/file.json.gz")).To(Equal(feedx.GZipCompression))
|
38
38
|
Expect(feedx.DetectCompression("/path/to/file.jsonz")).To(Equal(feedx.GZipCompression))
|
@@ -41,6 +41,9 @@ var _ = Describe("Compression", func() {
|
|
41
41
|
Expect(feedx.DetectCompression("/path/to/file.pb.gz")).To(Equal(feedx.GZipCompression))
|
42
42
|
Expect(feedx.DetectCompression("/path/to/file.pbz")).To(Equal(feedx.GZipCompression))
|
43
43
|
|
44
|
+
Expect(feedx.DetectCompression("/path/to/file.flate")).To(Equal(feedx.FlateCompression))
|
45
|
+
Expect(feedx.DetectCompression("/path/to/file.whatever.flate")).To(Equal(feedx.FlateCompression))
|
46
|
+
|
44
47
|
Expect(feedx.DetectCompression("")).To(Equal(feedx.NoCompression))
|
45
48
|
Expect(feedx.DetectCompression("/path/to/file")).To(Equal(feedx.NoCompression))
|
46
49
|
Expect(feedx.DetectCompression("/path/to/file.txt")).To(Equal(feedx.NoCompression))
|
@@ -50,7 +53,7 @@ var _ = Describe("Compression", func() {
|
|
50
53
|
var subject = feedx.NoCompression
|
51
54
|
var _ feedx.Compression = subject
|
52
55
|
|
53
|
-
It("
|
56
|
+
It("writes/reads", func() {
|
54
57
|
runSharedTest(subject)
|
55
58
|
})
|
56
59
|
})
|
@@ -59,7 +62,16 @@ var _ = Describe("Compression", func() {
|
|
59
62
|
var subject = feedx.GZipCompression
|
60
63
|
var _ feedx.Compression = subject
|
61
64
|
|
62
|
-
It("
|
65
|
+
It("writes/reads", func() {
|
66
|
+
runSharedTest(subject)
|
67
|
+
})
|
68
|
+
})
|
69
|
+
|
70
|
+
Describe("FlateCompression", func() {
|
71
|
+
var subject = feedx.FlateCompression
|
72
|
+
var _ feedx.Compression = subject
|
73
|
+
|
74
|
+
It("writes/reads", func() {
|
63
75
|
runSharedTest(subject)
|
64
76
|
})
|
65
77
|
})
|
data/consumer.go
CHANGED
@@ -50,6 +50,8 @@ type Consumer interface {
|
|
50
50
|
Data() interface{}
|
51
51
|
// LastSync returns time of last sync attempt.
|
52
52
|
LastSync() time.Time
|
53
|
+
// LastConsumed returns time of last feed consumption.
|
54
|
+
LastConsumed() time.Time
|
53
55
|
// LastModified returns time at which the remote feed was last modified.
|
54
56
|
LastModified() time.Time
|
55
57
|
// NumRead returns the number of values consumed during the last sync.
|
@@ -114,7 +116,7 @@ type consumer struct {
|
|
114
116
|
cfn ConsumeFunc
|
115
117
|
data atomic.Value
|
116
118
|
|
117
|
-
numRead, lastMod, lastSync int64
|
119
|
+
numRead, lastMod, lastSync, lastConsumed int64
|
118
120
|
}
|
119
121
|
|
120
122
|
// Data implements Consumer interface.
|
@@ -132,6 +134,11 @@ func (c *consumer) LastSync() time.Time {
|
|
132
134
|
return timestamp(atomic.LoadInt64(&c.lastSync)).Time()
|
133
135
|
}
|
134
136
|
|
137
|
+
// LastConsumed implements Consumer interface.
|
138
|
+
func (c *consumer) LastConsumed() time.Time {
|
139
|
+
return timestamp(atomic.LoadInt64(&c.lastConsumed)).Time()
|
140
|
+
}
|
141
|
+
|
135
142
|
// LastModified implements Consumer interface.
|
136
143
|
func (c *consumer) LastModified() time.Time {
|
137
144
|
return timestamp(atomic.LoadInt64(&c.lastMod)).Time()
|
@@ -147,8 +154,9 @@ func (c *consumer) Close() error {
|
|
147
154
|
}
|
148
155
|
|
149
156
|
func (c *consumer) sync(force bool) (*ConsumerSync, error) {
|
157
|
+
syncTime := timestampFromTime(time.Now()).Millis()
|
150
158
|
defer func() {
|
151
|
-
atomic.StoreInt64(&c.lastSync,
|
159
|
+
atomic.StoreInt64(&c.lastSync, syncTime)
|
152
160
|
}()
|
153
161
|
|
154
162
|
// retrieve original last modified time
|
@@ -158,7 +166,7 @@ func (c *consumer) sync(force bool) (*ConsumerSync, error) {
|
|
158
166
|
}
|
159
167
|
|
160
168
|
// skip update if not forced or modified
|
161
|
-
if lastMod.Millis() == atomic.LoadInt64(&c.lastMod)
|
169
|
+
if !force && lastMod > 0 && lastMod.Millis() == atomic.LoadInt64(&c.lastMod) {
|
162
170
|
return &ConsumerSync{Consumer: c}, nil
|
163
171
|
}
|
164
172
|
|
@@ -180,6 +188,7 @@ func (c *consumer) sync(force bool) (*ConsumerSync, error) {
|
|
180
188
|
c.data.Store(data)
|
181
189
|
atomic.StoreInt64(&c.numRead, int64(reader.NumRead()))
|
182
190
|
atomic.StoreInt64(&c.lastMod, lastMod.Millis())
|
191
|
+
atomic.StoreInt64(&c.lastConsumed, syncTime)
|
183
192
|
return &ConsumerSync{
|
184
193
|
Consumer: c,
|
185
194
|
Updated: true,
|
data/consumer_test.go
CHANGED
@@ -8,8 +8,8 @@ import (
|
|
8
8
|
"github.com/bsm/bfs"
|
9
9
|
"github.com/bsm/feedx"
|
10
10
|
"github.com/bsm/feedx/internal/testdata"
|
11
|
-
. "github.com/
|
12
|
-
. "github.com/
|
11
|
+
. "github.com/bsm/ginkgo"
|
12
|
+
. "github.com/bsm/gomega"
|
13
13
|
)
|
14
14
|
|
15
15
|
var _ = Describe("Consumer", func() {
|
@@ -17,25 +17,26 @@ var _ = Describe("Consumer", func() {
|
|
17
17
|
var obj *bfs.Object
|
18
18
|
var ctx = context.Background()
|
19
19
|
|
20
|
+
consume := func(r *feedx.Reader) (interface{}, error) {
|
21
|
+
var msgs []*testdata.MockMessage
|
22
|
+
for {
|
23
|
+
var msg testdata.MockMessage
|
24
|
+
if err := r.Decode(&msg); err == io.EOF {
|
25
|
+
break
|
26
|
+
} else if err != nil {
|
27
|
+
return nil, err
|
28
|
+
}
|
29
|
+
msgs = append(msgs, &msg)
|
30
|
+
}
|
31
|
+
return msgs, nil
|
32
|
+
}
|
33
|
+
|
20
34
|
BeforeEach(func() {
|
21
35
|
obj = bfs.NewInMemObject("path/to/file.jsonz")
|
22
|
-
Expect(writeMulti(obj, 2)).To(Succeed())
|
36
|
+
Expect(writeMulti(obj, 2, mockTime)).To(Succeed())
|
23
37
|
|
24
38
|
var err error
|
25
|
-
subject, err = feedx.NewConsumerForRemote(ctx, obj, nil,
|
26
|
-
var msgs []*testdata.MockMessage
|
27
|
-
for {
|
28
|
-
var msg testdata.MockMessage
|
29
|
-
if err := r.Decode(&msg); err == io.EOF {
|
30
|
-
break
|
31
|
-
}
|
32
|
-
if err != nil {
|
33
|
-
return nil, err
|
34
|
-
}
|
35
|
-
msgs = append(msgs, &msg)
|
36
|
-
}
|
37
|
-
return msgs, nil
|
38
|
-
})
|
39
|
+
subject, err = feedx.NewConsumerForRemote(ctx, obj, nil, consume)
|
39
40
|
Expect(err).NotTo(HaveOccurred())
|
40
41
|
})
|
41
42
|
|
@@ -43,11 +44,41 @@ var _ = Describe("Consumer", func() {
|
|
43
44
|
Expect(subject.Close()).To(Succeed())
|
44
45
|
})
|
45
46
|
|
46
|
-
It("
|
47
|
+
It("syncs/retrieves feeds from remote", func() {
|
47
48
|
Expect(subject.LastSync()).To(BeTemporally("~", time.Now(), time.Second))
|
48
|
-
Expect(subject.
|
49
|
+
Expect(subject.LastConsumed()).To(BeTemporally("==", subject.LastSync()))
|
50
|
+
Expect(subject.LastModified()).To(BeTemporally("==", mockTime.Truncate(time.Millisecond)))
|
49
51
|
Expect(subject.NumRead()).To(Equal(2))
|
50
52
|
Expect(subject.Data()).To(ConsistOf(seed(), seed()))
|
51
53
|
Expect(subject.Close()).To(Succeed())
|
52
54
|
})
|
55
|
+
|
56
|
+
It("consumes feeds only if necessary", func() {
|
57
|
+
prevSync := subject.LastSync()
|
58
|
+
time.Sleep(2 * time.Millisecond)
|
59
|
+
|
60
|
+
testable := subject.(interface{ TestSync() error })
|
61
|
+
Expect(testable.TestSync()).To(Succeed())
|
62
|
+
Expect(subject.LastSync()).To(BeTemporally(">", prevSync))
|
63
|
+
Expect(subject.LastConsumed()).To(BeTemporally("==", prevSync)) // skipped on last sync
|
64
|
+
Expect(subject.LastModified()).To(BeTemporally("==", mockTime.Truncate(time.Millisecond)))
|
65
|
+
Expect(subject.NumRead()).To(Equal(2))
|
66
|
+
})
|
67
|
+
|
68
|
+
It("always consumes if LastModified not set", func() {
|
69
|
+
noModTime := bfs.NewInMemObject("path/to/file.json")
|
70
|
+
Expect(writeMulti(noModTime, 2, time.Time{})).To(Succeed())
|
71
|
+
|
72
|
+
csmr, err := feedx.NewConsumerForRemote(ctx, noModTime, nil, consume)
|
73
|
+
Expect(err).NotTo(HaveOccurred())
|
74
|
+
|
75
|
+
prevSync := csmr.LastSync()
|
76
|
+
time.Sleep(2 * time.Millisecond)
|
77
|
+
|
78
|
+
testable := csmr.(interface{ TestSync() error })
|
79
|
+
Expect(testable.TestSync()).To(Succeed())
|
80
|
+
Expect(csmr.LastSync()).To(BeTemporally(">", prevSync))
|
81
|
+
Expect(csmr.LastConsumed()).To(BeTemporally("==", csmr.LastSync())) // consumed on last sync
|
82
|
+
Expect(csmr.LastModified()).To(BeTemporally("==", time.Unix(0, 0)))
|
83
|
+
})
|
53
84
|
})
|
@@ -0,0 +1,59 @@
|
|
1
|
+
package parquet
|
2
|
+
|
3
|
+
import (
|
4
|
+
"io"
|
5
|
+
|
6
|
+
goparquet "github.com/fraugster/parquet-go"
|
7
|
+
"github.com/fraugster/parquet-go/floor"
|
8
|
+
)
|
9
|
+
|
10
|
+
type decoder struct {
|
11
|
+
pfr *goparquet.FileReader
|
12
|
+
ffr *floor.Reader
|
13
|
+
tmp *tempFile
|
14
|
+
}
|
15
|
+
|
16
|
+
func newDecoder(rs io.ReadSeeker) (*decoder, error) {
|
17
|
+
pfr, err := goparquet.NewFileReader(rs)
|
18
|
+
if err != nil {
|
19
|
+
return nil, err
|
20
|
+
}
|
21
|
+
|
22
|
+
ffr := floor.NewReader(pfr)
|
23
|
+
|
24
|
+
return &decoder{
|
25
|
+
pfr: pfr,
|
26
|
+
ffr: ffr,
|
27
|
+
},
|
28
|
+
nil
|
29
|
+
}
|
30
|
+
|
31
|
+
func (w *decoder) Decode(v interface{}) error {
|
32
|
+
// read the next value and scan
|
33
|
+
if w.ffr.Next() {
|
34
|
+
return w.ffr.Scan(v)
|
35
|
+
}
|
36
|
+
|
37
|
+
// check for errors
|
38
|
+
if err := w.ffr.Err(); err != nil {
|
39
|
+
return err
|
40
|
+
}
|
41
|
+
|
42
|
+
// end of file
|
43
|
+
return io.EOF
|
44
|
+
}
|
45
|
+
|
46
|
+
func (w *decoder) Close() (err error) {
|
47
|
+
// close the tmp file if present
|
48
|
+
if w.tmp != nil {
|
49
|
+
if e := w.tmp.Close(); e != nil {
|
50
|
+
err = e
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
// close the reader
|
55
|
+
if e := w.ffr.Close(); e != nil {
|
56
|
+
err = e
|
57
|
+
}
|
58
|
+
return
|
59
|
+
}
|