Skip to content

Commit 45bd645

Browse files
author
Jason Yellick
committed
Abstract out a rawledger interface
This also removes the ramledger implementation from solo, and turns it into its own package, utilizing the new rawledger interface. https://jira.hyperledger.org/browse/FAB-325 Change-Id: I1993349e096fe1664089a919f955da1ab57e58a0 Signed-off-by: Jason Yellick <[email protected]>
1 parent 543baa3 commit 45bd645

10 files changed

+337
-183
lines changed
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ramledger
18+
19+
import (
20+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
21+
"github.com/hyperledger/fabric/orderer/rawledger"
22+
23+
"github.com/op/go-logging"
24+
)
25+
26+
var logger = logging.MustGetLogger("rawledger/ramledger")
27+
28+
func init() {
29+
logging.SetLevel(logging.DEBUG, "")
30+
}
31+
32+
type cursor struct {
33+
list *simpleList
34+
}
35+
36+
type simpleList struct {
37+
next *simpleList
38+
signal chan struct{}
39+
block *ab.Block
40+
}
41+
42+
type ramLedger struct {
43+
maxSize int
44+
size int
45+
oldest *simpleList
46+
newest *simpleList
47+
}
48+
49+
// New creates a new instance of the ram ledger
50+
func New(maxSize int) rawledger.ReadWriter {
51+
rl := &ramLedger{
52+
maxSize: maxSize,
53+
size: 1,
54+
oldest: &simpleList{
55+
signal: make(chan struct{}),
56+
block: &ab.Block{
57+
Number: 0,
58+
PrevHash: []byte("GENESIS"),
59+
},
60+
},
61+
}
62+
rl.newest = rl.oldest
63+
return rl
64+
}
65+
66+
// Height returns the highest block number in the chain, plus one
67+
func (rl *ramLedger) Height() uint64 {
68+
return rl.newest.block.Number + 1
69+
}
70+
71+
// Iterator implements the rawledger.Reader definition
72+
func (rl *ramLedger) Iterator(startType ab.SeekInfo_StartType, specified uint64) (rawledger.Iterator, uint64) {
73+
var list *simpleList
74+
switch startType {
75+
case ab.SeekInfo_OLDEST:
76+
oldest := rl.oldest
77+
list = &simpleList{
78+
block: &ab.Block{Number: oldest.block.Number - 1},
79+
next: oldest,
80+
signal: make(chan struct{}),
81+
}
82+
close(list.signal)
83+
case ab.SeekInfo_NEWEST:
84+
newest := rl.newest
85+
list = &simpleList{
86+
block: &ab.Block{Number: newest.block.Number - 1},
87+
next: newest,
88+
signal: make(chan struct{}),
89+
}
90+
close(list.signal)
91+
case ab.SeekInfo_SPECIFIED:
92+
list = rl.oldest
93+
if specified < list.block.Number || specified > rl.newest.block.Number+1 {
94+
return &rawledger.NotFoundErrorIterator{}, 0
95+
}
96+
97+
for {
98+
if list.block.Number == specified-1 {
99+
break
100+
}
101+
list = list.next // No need for nil check, because of range check above
102+
}
103+
}
104+
return &cursor{list: list}, list.block.Number + 1
105+
}
106+
107+
// Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable
108+
func (cu *cursor) Next() (*ab.Block, ab.Status) {
109+
// This only loops once, as signal reading indicates non-nil next
110+
for {
111+
if cu.list.next != nil {
112+
cu.list = cu.list.next
113+
return cu.list.block, ab.Status_SUCCESS
114+
}
115+
116+
<-cu.list.signal
117+
}
118+
}
119+
120+
// ReadyChan returns a channel that will close when Next is ready to be called without blocking
121+
func (cu *cursor) ReadyChan() <-chan struct{} {
122+
return cu.list.signal
123+
}
124+
125+
// Append creates a new block and appends it to the ledger
126+
func (rl *ramLedger) Append(messages []*ab.BroadcastMessage, proof []byte) *ab.Block {
127+
block := &ab.Block{
128+
Number: rl.newest.block.Number + 1,
129+
PrevHash: rl.newest.block.Hash(),
130+
Messages: messages,
131+
Proof: proof,
132+
}
133+
rl.appendBlock(block)
134+
return block
135+
}
136+
137+
func (rl *ramLedger) appendBlock(block *ab.Block) {
138+
rl.newest.next = &simpleList{
139+
signal: make(chan struct{}),
140+
block: block,
141+
}
142+
143+
lastSignal := rl.newest.signal
144+
logger.Debugf("Sending signal that block %d has a successor", rl.newest.block.Number)
145+
rl.newest = rl.newest.next
146+
close(lastSignal)
147+
148+
rl.size++
149+
150+
if rl.size > rl.maxSize {
151+
rl.oldest = rl.oldest.next
152+
rl.size--
153+
}
154+
}

orderer/solo/ramledger_test.go orderer/rawledger/ramledger/ramledger_test.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package solo
17+
package ramledger
1818

1919
import (
2020
"testing"
@@ -23,9 +23,10 @@ import (
2323
)
2424

2525
// TestAppend ensures that appending blocks stores only the maxSize most recent blocks
26+
// Note that 'only' is applicable because the genesis block will be discarded
2627
func TestAppend(t *testing.T) {
2728
maxSize := 3
28-
rl := newRAMLedger(maxSize)
29+
rl := New(maxSize).(*ramLedger)
2930
var blocks []*ab.Block
3031
for i := 0; i < 3; i++ {
3132
blocks = append(blocks, &ab.Block{Number: uint64(i + 1)})
@@ -50,7 +51,7 @@ func TestAppend(t *testing.T) {
5051
// TestSignal checks if the signal channel closes when an item is appended
5152
func TestSignal(t *testing.T) {
5253
maxSize := 3
53-
rl := newRAMLedger(maxSize)
54+
rl := New(maxSize).(*ramLedger)
5455
item := rl.newest
5556
select {
5657
case <-item.signal:
@@ -66,12 +67,12 @@ func TestSignal(t *testing.T) {
6667
}
6768

6869
// TestTruncatingSafety is intended to simulate a reader who fetches a reference to the oldest list item
69-
// which is then pushed off the history by appending greater than the history size (here, 10 appeneds with
70+
// which is then pushed off the history by appending greater than the history size (here, 10 appends with
7071
// a maxSize of 3). We let the go garbage collector ensure the references still exist
7172
func TestTruncationSafety(t *testing.T) {
7273
maxSize := 3
7374
newBlocks := 10
74-
rl := newRAMLedger(maxSize)
75+
rl := New(maxSize).(*ramLedger)
7576
item := rl.oldest
7677
for i := 0; i < newBlocks; i++ {
7778
rl.appendBlock(&ab.Block{Number: uint64(i + 1)})
@@ -83,6 +84,6 @@ func TestTruncationSafety(t *testing.T) {
8384
}
8485

8586
if count != newBlocks {
86-
t.Fatalf("The iterator should have found 10 new blocks")
87+
t.Fatalf("The iterator should have found %d new blocks but found %d", newBlocks, count)
8788
}
8889
}

orderer/rawledger/rawledger.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package rawledger
18+
19+
import (
20+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
21+
)
22+
23+
// Iterator is useful for a chain Reader to stream blocks as they are created
24+
type Iterator interface {
25+
// Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable
26+
Next() (*ab.Block, ab.Status)
27+
// ReadyChan supplies a channel which will block until Next will not block
28+
ReadyChan() <-chan struct{}
29+
}
30+
31+
// Reader allows the caller to inspect the raw ledger
32+
type Reader interface {
33+
// Iterator retrieves an Iterator, as specified by an ab.SeekInfo message, returning an iterator, and it's starting block number
34+
Iterator(startType ab.SeekInfo_StartType, specified uint64) (Iterator, uint64)
35+
// Height returns the highest block number in the chain, plus one
36+
Height() uint64
37+
}
38+
39+
// Writer allows the caller to modify the raw ledger
40+
type Writer interface {
41+
// Append a new block to the ledger
42+
Append(blockContents []*ab.BroadcastMessage, proof []byte) *ab.Block
43+
}
44+
45+
// ReadWriter encapsulated both the reading and writing functions of the rawledger
46+
type ReadWriter interface {
47+
Reader
48+
Writer
49+
}

orderer/rawledger/util.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package rawledger
18+
19+
import (
20+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
21+
)
22+
23+
var closedChan chan struct{}
24+
25+
func init() {
26+
closedChan = make(chan struct{})
27+
close(closedChan)
28+
}
29+
30+
// NotFoundErrorIterator simply always returns an error of ab.Status_NOT_FOUND, and is generally useful for implementations of the Reader interface
31+
type NotFoundErrorIterator struct{}
32+
33+
// Next returns nil, ab.Status_NOT_FOUND
34+
func (nfei *NotFoundErrorIterator) Next() (*ab.Block, ab.Status) {
35+
return nil, ab.Status_NOT_FOUND
36+
}
37+
38+
// ReadyChan returns a closed channel
39+
func (nfei *NotFoundErrorIterator) ReadyChan() <-chan struct{} {
40+
return closedChan
41+
}

orderer/solo/broadcast.go

+24-26
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,30 @@ import (
2020
"time"
2121

2222
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
23+
"github.com/hyperledger/fabric/orderer/rawledger"
2324
)
2425

2526
type broadcastServer struct {
2627
queue chan *ab.BroadcastMessage
2728
batchSize int
2829
batchTimeout time.Duration
29-
rl *ramLedger
30+
rl rawledger.Writer
3031
exitChan chan struct{}
3132
}
3233

33-
func newBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rs *ramLedger) *broadcastServer {
34-
bs := newPlainBroadcastServer(queueSize, batchSize, batchTimeout, rs)
35-
bs.exitChan = make(chan struct{})
34+
func newBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer) *broadcastServer {
35+
bs := newPlainBroadcastServer(queueSize, batchSize, batchTimeout, rl)
3636
go bs.main()
3737
return bs
3838
}
3939

40-
func newPlainBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl *ramLedger) *broadcastServer {
40+
func newPlainBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer) *broadcastServer {
4141
bs := &broadcastServer{
4242
queue: make(chan *ab.BroadcastMessage, queueSize),
4343
batchSize: batchSize,
4444
batchTimeout: batchTimeout,
4545
rl: rl,
46+
exitChan: make(chan struct{}),
4647
}
4748
return bs
4849
}
@@ -56,31 +57,28 @@ func (bs *broadcastServer) main() {
5657
outer:
5758
for {
5859
timer := time.After(bs.batchTimeout)
59-
select {
60-
case msg := <-bs.queue:
61-
curBatch = append(curBatch, msg)
62-
if len(curBatch) < bs.batchSize {
63-
continue
64-
}
65-
logger.Debugf("Batch size met, creating block")
66-
case <-timer:
67-
if len(curBatch) == 0 {
68-
continue outer
60+
for {
61+
select {
62+
case msg := <-bs.queue:
63+
curBatch = append(curBatch, msg)
64+
if len(curBatch) < bs.batchSize {
65+
continue
66+
}
67+
logger.Debugf("Batch size met, creating block")
68+
case <-timer:
69+
if len(curBatch) == 0 {
70+
continue outer
71+
}
72+
logger.Debugf("Batch timer expired, creating block")
73+
case <-bs.exitChan:
74+
logger.Debugf("Exiting")
75+
return
6976
}
70-
logger.Debugf("Batch timer expired, creating block")
71-
case <-bs.exitChan:
72-
logger.Debugf("Exiting")
73-
return
77+
break
7478
}
7579

76-
block := &ab.Block{
77-
Number: bs.rl.newest.block.Number + 1,
78-
PrevHash: bs.rl.newest.block.Hash(),
79-
Messages: curBatch,
80-
}
80+
bs.rl.Append(curBatch, nil)
8181
curBatch = nil
82-
83-
bs.rl.appendBlock(block)
8482
}
8583
}
8684

0 commit comments

Comments
 (0)