Skip to content

Commit 6eab9cf

Browse files
author
Jason Yellick
committed
[FAB-5459] Recompute configmap instead of updating
The configtx validation code works by transforming the ConfigGroup tree structure into a map, where each config element has a fully qualified key like: [Groups] /Channel/Application or [Policy] /Channel/Application/Readers This flattened structure makes it easier to check which elements have been modified, as the maps may simply be iterated over, rather than walking the config tree. After applying a config update, the current config map is copied, and the elements which were updated are overlayed onto the old config. This map is then converted back into a tree structure and serialized to be the new config tree. The current code adopts the updated config map as the current config map. However, this produces the bug described in 5459. Because the updated config map is the overlay of the new config onto the old config, the updated config may contain orphaned nodes which appear in the map, but which do not appear in the config tree. Consequently, when a subsequent update arrives, it is validated not only against the current config, but also against the orphaned nodes which are still in the updated config map. This CR changes the logic to discard the updated config map (which may contain this orphaned nodes) and instead has the config map recomputed every time a new config is adopted. This is more obviously deterministic and mimics the way a new ordering node would initialize the config after being restarted. Note: There is no accompanying test case with this. I had originally written a test case which demonstrated that nodes were orphaned in the updated config. However, this is expected and not a useful test case. Similarly, forcing the update config map to have updated nodes, then testing that that map is not adopted does not provide a valuable test case. So, instead of a test, this CR opts for some code comments and this lengthly commit explaining the change. Change-Id: Idc847cd5e237531e4a8b978f3465e30a909eb94f Signed-off-by: Jason Yellick <[email protected]>
1 parent 7a480ce commit 6eab9cf

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

common/configtx/manager.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -204,49 +204,49 @@ func (cm *configManager) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigE
204204
}, nil
205205
}
206206

207-
func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) {
207+
func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (*configResult, error) {
208208
if configEnv == nil {
209-
return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope")
209+
return nil, fmt.Errorf("Attempted to apply config with nil envelope")
210210
}
211211

212212
if configEnv.Config == nil {
213-
return nil, nil, fmt.Errorf("Config cannot be nil")
213+
return nil, fmt.Errorf("Config cannot be nil")
214214
}
215215

216216
if configEnv.Config.Sequence != cm.current.sequence+1 {
217-
return nil, nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence)
217+
return nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence)
218218
}
219219

220220
configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate)
221221
if err != nil {
222-
return nil, nil, err
222+
return nil, err
223223
}
224224

225225
configMap, err := cm.authorizeUpdate(configUpdateEnv)
226226
if err != nil {
227-
return nil, nil, err
227+
return nil, err
228228
}
229229

230230
channelGroup, err := configMapToConfig(configMap)
231231
if err != nil {
232-
return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
232+
return nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
233233
}
234234

235235
if !reflect.DeepEqual(channelGroup, configEnv.Config.ChannelGroup) {
236-
return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
236+
return nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
237237
}
238238

239239
result, err := cm.processConfig(channelGroup)
240240
if err != nil {
241-
return nil, nil, err
241+
return nil, err
242242
}
243243

244-
return configMap, result, nil
244+
return result, nil
245245
}
246246

247247
// Validate simulates applying a ConfigEnvelope to become the new config
248248
func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error {
249-
_, result, err := cm.prepareApply(configEnv)
249+
result, err := cm.prepareApply(configEnv)
250250
if err != nil {
251251
return err
252252
}
@@ -258,7 +258,17 @@ func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error {
258258

259259
// Apply attempts to apply a ConfigEnvelope to become the new config
260260
func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error {
261-
configMap, result, err := cm.prepareApply(configEnv)
261+
// Note, although prepareApply will necessarilly compute a config map
262+
// for the updated config, this config map will possibly contain unreachable
263+
// elements from a config graph perspective. Therefore, it is not safe to use
264+
// as the config map after application. Instead, we compute the config map
265+
// just like we would at startup.
266+
configMap, err := MapConfig(configEnv.Config.ChannelGroup)
267+
if err != nil {
268+
return err
269+
}
270+
271+
result, err := cm.prepareApply(configEnv)
262272
if err != nil {
263273
return err
264274
}

0 commit comments

Comments
 (0)