Skip to content

Commit 2a6a7b5

Browse files
author
Jason Yellick
committed
[FAB-2511] Make configtx sequence explicit
https://jira.hyperledger.org/browse/FAB-2511 The previous non-MVCC configuration system required that the modified version numbers of the configuration elements be equal to the global configuration sequence lock. This makes the global sequence explicit, while allowing the individual elements to move (or not move) their version numbers locally. This finally enables the partial update functionality advertised in FAB-1880. Change-Id: Ic2f554b864e91435a8812d56eb8247cc1fa33720 Signed-off-by: Jason Yellick <[email protected]>
1 parent 3000b25 commit 2a6a7b5

File tree

7 files changed

+263
-169
lines changed

7 files changed

+263
-169
lines changed

common/configtx/api/api.go

-3
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ type Manager interface {
3737
// Validate attempts to validate a new configtx against the current config state
3838
ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error)
3939

40-
// ConfigEnvelope returns the *cb.ConfigEnvelope from the last successful Apply
41-
ConfigEnvelope() *cb.ConfigEnvelope
42-
4340
// ChainID retrieves the chain ID associated with this manager
4441
ChainID() string
4542

common/configtx/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handle
6969
i++
7070
}
7171

72-
logger.Debugf("Beginning new config for channel %s and group %s", cm.chainID, name)
72+
logger.Debugf("Beginning new config for channel %s and group %s", cm.current.channelID, name)
7373
subHandlers, err := handler.BeginValueProposals(subGroups)
7474
if err != nil {
7575
return nil, err

common/configtx/configmap.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ const (
3737
// it takes a ConfigGroup and generates a map of fqPath to comparables (or error on invalid keys)
3838
func mapConfig(channelGroup *cb.ConfigGroup) (map[string]comparable, error) {
3939
result := make(map[string]comparable)
40-
err := recurseConfig(result, []string{RootGroupKey}, channelGroup)
41-
if err != nil {
42-
return nil, err
40+
if channelGroup != nil {
41+
err := recurseConfig(result, []string{RootGroupKey}, channelGroup)
42+
if err != nil {
43+
return nil, err
44+
}
4345
}
4446
return result, nil
4547
}

common/configtx/manager.go

+35-44
Original file line numberDiff line numberDiff line change
@@ -40,34 +40,17 @@ var (
4040
}
4141
)
4242

43-
// NewConfigItemPolicyKey is the ID of the policy used when no other policy can be resolved, for instance when attempting to create a new config item
44-
const NewConfigItemPolicyKey = "NewConfigItemPolicy"
43+
type config struct {
44+
channelID string
45+
sequence uint64
46+
configMap map[string]comparable
47+
}
4548

4649
type configManager struct {
4750
api.Resources
48-
sequence uint64
49-
chainID string
50-
config map[string]comparable
5151
callOnUpdate []func(api.Manager)
5252
initializer api.Initializer
53-
configEnv *cb.ConfigEnvelope
54-
}
55-
56-
func computeSequence(configGroup *cb.ConfigGroup) uint64 {
57-
max := uint64(0)
58-
for _, value := range configGroup.Values {
59-
if value.Version > max {
60-
max = value.Version
61-
}
62-
}
63-
64-
for _, group := range configGroup.Groups {
65-
if groupMax := computeSequence(group); groupMax > max {
66-
max = groupMax
67-
}
68-
}
69-
70-
return max
53+
current *config
7154
}
7255

7356
// validateChainID makes sure that proposed chain IDs (i.e. channel names)
@@ -125,11 +108,13 @@ func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnU
125108
}
126109

127110
cm := &configManager{
128-
Resources: initializer,
129-
initializer: initializer,
130-
sequence: configEnv.Config.Sequence,
131-
chainID: header.ChannelId,
132-
config: configMap,
111+
Resources: initializer,
112+
initializer: initializer,
113+
current: &config{
114+
sequence: configEnv.Config.Sequence,
115+
configMap: configMap,
116+
channelID: header.ChannelId,
117+
},
133118
callOnUpdate: callOnUpdate,
134119
}
135120

@@ -149,9 +134,13 @@ func (cm *configManager) commitCallbacks() {
149134
}
150135
}
151136

152-
// Validate attempts to validate a new configtx against the current config state
153-
// It requires an Envelope of type CONFIG_UPDATE
137+
// ProposeConfigUpdate takes in an Envelope of type CONFIG_UPDATE and produces a
138+
// ConfigEnvelope to be used as the Envelope Payload Data of a CONFIG message
154139
func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
140+
return cm.proposeConfigUpdate(configtx)
141+
}
142+
143+
func (cm *configManager) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
155144
configUpdateEnv, err := envelopeToConfigUpdate(configtx)
156145
if err != nil {
157146
return nil, err
@@ -176,7 +165,7 @@ func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigE
176165

177166
return &cb.ConfigEnvelope{
178167
Config: &cb.Config{
179-
Sequence: cm.sequence,
168+
Sequence: cm.current.sequence + 1,
180169
ChannelGroup: channelGroup,
181170
},
182171
LastUpdate: configtx,
@@ -188,6 +177,14 @@ func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]
188177
return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope")
189178
}
190179

180+
if configEnv.Config == nil {
181+
return nil, nil, fmt.Errorf("Config cannot be nil")
182+
}
183+
184+
if configEnv.Config.Sequence != cm.current.sequence+1 {
185+
return nil, nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence)
186+
}
187+
191188
configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate)
192189
if err != nil {
193190
return nil, nil, err
@@ -203,10 +200,6 @@ func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]
203200
return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
204201
}
205202

206-
if configEnv.Config == nil {
207-
return nil, nil, fmt.Errorf("Config cannot be nil")
208-
}
209-
210203
if !reflect.DeepEqual(channelGroup, configEnv.Config.ChannelGroup) {
211204
return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
212205
}
@@ -241,23 +234,21 @@ func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error {
241234
result.commit()
242235
cm.commitCallbacks()
243236

244-
cm.config = configMap
245-
cm.sequence++
237+
cm.current = &config{
238+
configMap: configMap,
239+
channelID: cm.current.channelID,
240+
sequence: configEnv.Config.Sequence,
241+
}
246242

247243
return nil
248244
}
249245

250-
// ConfigEnvelope retrieve the current ConfigEnvelope, generated after the last successfully applied configuration
251-
func (cm *configManager) ConfigEnvelope() *cb.ConfigEnvelope {
252-
return cm.configEnv
253-
}
254-
255246
// ChainID retrieves the chain ID associated with this manager
256247
func (cm *configManager) ChainID() string {
257-
return cm.chainID
248+
return cm.current.channelID
258249
}
259250

260251
// Sequence returns the current sequence number of the config
261252
func (cm *configManager) Sequence() uint64 {
262-
return cm.sequence
253+
return cm.current.sequence
263254
}

common/configtx/manager_test.go

+41-35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
2626
cb "github.com/hyperledger/fabric/protos/common"
2727
"github.com/hyperledger/fabric/protos/utils"
28+
29+
"github.com/stretchr/testify/assert"
2830
)
2931

3032
var defaultChain = "DefaultChainID"
@@ -62,9 +64,9 @@ func makeConfigPair(id, modificationPolicy string, lastModified uint64, data []b
6264
}
6365

6466
func makeEnvelopeConfig(channelID string, configPairs ...*configPair) *cb.Envelope {
65-
values := make(map[string]*cb.ConfigValue)
67+
channelGroup := cb.NewConfigGroup()
6668
for _, pair := range configPairs {
67-
values[pair.key] = pair.value
69+
channelGroup.Values[pair.key] = pair.value
6870
}
6971

7072
return &cb.Envelope{
@@ -77,27 +79,22 @@ func makeEnvelopeConfig(channelID string, configPairs ...*configPair) *cb.Envelo
7779
},
7880
Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{
7981
Config: &cb.Config{
80-
ChannelGroup: &cb.ConfigGroup{
81-
Values: values,
82-
},
82+
ChannelGroup: channelGroup,
8383
},
8484
}),
8585
}),
8686
}
8787
}
8888

89-
func makeConfigUpdateEnvelope(chainID string, configPairs ...*configPair) *cb.Envelope {
90-
values := make(map[string]*cb.ConfigValue)
89+
func makeConfigSet(configPairs ...*configPair) *cb.ConfigGroup {
90+
result := cb.NewConfigGroup()
9191
for _, pair := range configPairs {
92-
values[pair.key] = pair.value
92+
result.Values[pair.key] = pair.value
9393
}
94+
return result
95+
}
9496

95-
config := &cb.ConfigUpdate{
96-
ChannelId: chainID,
97-
WriteSet: &cb.ConfigGroup{
98-
Values: values,
99-
},
100-
}
97+
func makeConfigUpdateEnvelope(chainID string, readSet, writeSet *cb.ConfigGroup) *cb.Envelope {
10198
return &cb.Envelope{
10299
Payload: utils.MarshalOrPanic(&cb.Payload{
103100
Header: &cb.Header{
@@ -106,7 +103,11 @@ func makeConfigUpdateEnvelope(chainID string, configPairs ...*configPair) *cb.En
106103
}),
107104
},
108105
Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
109-
ConfigUpdate: utils.MarshalOrPanic(config),
106+
ConfigUpdate: utils.MarshalOrPanic(&cb.ConfigUpdate{
107+
ChannelId: chainID,
108+
ReadSet: readSet,
109+
WriteSet: writeSet,
110+
}),
110111
}),
111112
}),
112113
}
@@ -141,7 +142,7 @@ func TestDifferentChainID(t *testing.T) {
141142
t.Fatalf("Error constructing config manager: %s", err)
142143
}
143144

144-
newConfig := makeConfigUpdateEnvelope("wrongChain", makeConfigPair("foo", "foo", 1, []byte("foo")))
145+
newConfig := makeConfigUpdateEnvelope("wrongChain", makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
145146

146147
_, err = cm.ProposeConfigUpdate(newConfig)
147148
if err == nil {
@@ -159,7 +160,7 @@ func TestOldConfigReplay(t *testing.T) {
159160
t.Fatalf("Error constructing config manager: %s", err)
160161
}
161162

162-
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")))
163+
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))))
163164

164165
_, err = cm.ProposeConfigUpdate(newConfig)
165166
if err == nil {
@@ -177,7 +178,7 @@ func TestValidConfigChange(t *testing.T) {
177178
t.Fatalf("Error constructing config manager: %s", err)
178179
}
179180

180-
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo")))
181+
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
181182

182183
configEnv, err := cm.ProposeConfigUpdate(newConfig)
183184
if err != nil {
@@ -208,8 +209,8 @@ func TestConfigChangeRegressedSequence(t *testing.T) {
208209

209210
newConfig := makeConfigUpdateEnvelope(
210211
defaultChain,
211-
makeConfigPair("foo", "foo", 0, []byte("foo")),
212-
makeConfigPair("bar", "bar", 2, []byte("bar")),
212+
makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))),
213+
makeConfigSet(makeConfigPair("bar", "bar", 2, []byte("bar"))),
213214
)
214215

215216
_, err = cm.ProposeConfigUpdate(newConfig)
@@ -231,8 +232,11 @@ func TestConfigChangeOldSequence(t *testing.T) {
231232

232233
newConfig := makeConfigUpdateEnvelope(
233234
defaultChain,
234-
makeConfigPair("foo", "foo", 2, []byte("foo")),
235-
makeConfigPair("bar", "bar", 1, []byte("bar")),
235+
makeConfigSet(),
236+
makeConfigSet(
237+
makeConfigPair("foo", "foo", 2, []byte("foo")),
238+
makeConfigPair("bar", "bar", 1, []byte("bar")),
239+
),
236240
)
237241

238242
_, err = cm.ProposeConfigUpdate(newConfig)
@@ -241,9 +245,9 @@ func TestConfigChangeOldSequence(t *testing.T) {
241245
}
242246
}
243247

244-
// TestConfigImplicitDelete tests to make sure that a new config does not implicitly delete config items
245-
// by omitting them in the new config
246-
func TestConfigImplicitDelete(t *testing.T) {
248+
// TestConfigPartialUpdate tests to make sure that a new config can set only part
249+
// of the config and still be accepted
250+
func TestConfigPartialUpdate(t *testing.T) {
247251
cm, err := NewManagerImpl(
248252
makeEnvelopeConfig(
249253
defaultChain,
@@ -258,13 +262,12 @@ func TestConfigImplicitDelete(t *testing.T) {
258262

259263
newConfig := makeConfigUpdateEnvelope(
260264
defaultChain,
261-
makeConfigPair("bar", "bar", 1, []byte("bar")),
265+
makeConfigSet(),
266+
makeConfigSet(makeConfigPair("bar", "bar", 1, []byte("bar"))),
262267
)
263268

264269
_, err = cm.ProposeConfigUpdate(newConfig)
265-
if err == nil {
266-
t.Error("Should have errored proposing config because foo was implicitly deleted")
267-
}
270+
assert.NoError(t, err, "Should have allowed partial update")
268271
}
269272

270273
// TestEmptyConfigUpdate tests to make sure that an empty config is rejected as an update
@@ -303,8 +306,11 @@ func TestSilentConfigModification(t *testing.T) {
303306

304307
newConfig := makeConfigUpdateEnvelope(
305308
defaultChain,
306-
makeConfigPair("foo", "foo", 0, []byte("different")),
307-
makeConfigPair("bar", "bar", 1, []byte("bar")),
309+
makeConfigSet(),
310+
makeConfigSet(
311+
makeConfigPair("foo", "foo", 0, []byte("different")),
312+
makeConfigPair("bar", "bar", 1, []byte("bar")),
313+
),
308314
)
309315

310316
_, err = cm.ProposeConfigUpdate(newConfig)
@@ -327,7 +333,7 @@ func TestConfigChangeViolatesPolicy(t *testing.T) {
327333
// Set the mock policy to error
328334
initializer.Resources.PolicyManagerVal.Policy.Err = fmt.Errorf("err")
329335

330-
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo")))
336+
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
331337

332338
_, err = cm.ProposeConfigUpdate(newConfig)
333339
if err == nil {
@@ -353,8 +359,8 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) {
353359

354360
newConfig := makeConfigUpdateEnvelope(
355361
defaultChain,
356-
makeConfigPair("foo", "foo", 0, []byte("foo")),
357-
makeConfigPair("bar", "bar", 1, []byte("foo")),
362+
makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))),
363+
makeConfigSet(makeConfigPair("bar", "bar", 0, []byte("foo"))),
358364
)
359365

360366
configEnv, err := cm.ProposeConfigUpdate(newConfig)
@@ -387,7 +393,7 @@ func TestInvalidProposal(t *testing.T) {
387393

388394
initializer.ValueProposerVal = &mockconfigtx.ValueProposer{ErrorForProposeConfig: fmt.Errorf("err")}
389395

390-
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo")))
396+
newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
391397

392398
_, err = cm.ProposeConfigUpdate(newConfig)
393399
if err == nil {

0 commit comments

Comments
 (0)