MM-14246 - Plugin framework: support transactional semantics with KV Store (#10634)

* MM-14246 - Plugin framework: support transactional semantics with KV Store

Rename old, new variable names

Moving New function to the bottom

* Made CompareAndUpdate sync, updated tests

* Removed going through channel in CompareAndSetPluginKey

* Inserting new key when oldValue is nil to KVCompareAndSet

* Updated error text to include CompareAndSet
Этот коммит содержится в:
Ali F
2019-04-23 13:35:17 -04:00
коммит произвёл Christopher Speller
родитель 446c6d452e
Коммит 6f8577b4c1
10 изменённых файлов: 311 добавлений и 0 удалений

Просмотреть файл

@@ -71,6 +71,51 @@ func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) store.StoreChann
})
}
func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) {
if err := kv.IsValid(); err != nil {
return false, err
}
if oldValue == nil {
// Insert if oldValue is nil
if err := ps.GetMaster().Insert(kv); err != nil {
// If the error is from unique constraints violation, it's the result of a
// race condition, return false and no error. Otherwise we have a real error and
// need to return it.
if IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey"}) {
return false, nil
} else {
return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
} else {
// Update if oldValue is not nil
updateResult, err := ps.GetMaster().Exec(
`UPDATE PluginKeyValueStore SET PValue = :New WHERE PluginId = :PluginId AND PKey = :Key AND PValue = :Old`,
map[string]interface{}{
"PluginId": kv.PluginId,
"Key": kv.Key,
"Old": oldValue,
"New": kv.Value,
},
)
if err != nil {
return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if rowsAffected, err := updateResult.RowsAffected(); err != nil {
// Failed to update
return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsAffected == 0 {
// No rows were affected by the update, where condition was not satisfied,
// return false, but no error.
return false, nil
}
}
return true, nil
}
func (ps SqlPluginStore) Get(pluginId, key string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var kv *model.PluginKeyValue

Просмотреть файл

@@ -524,6 +524,7 @@ type UserAccessTokenStore interface {
type PluginStore interface {
SaveOrUpdate(keyVal *model.PluginKeyValue) StoreChannel
CompareAndSet(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError)
Get(pluginId, key string) StoreChannel
Delete(pluginId, key string) StoreChannel
DeleteAllForPlugin(PluginId string) StoreChannel

Просмотреть файл

@@ -13,6 +13,29 @@ type PluginStore struct {
mock.Mock
}
// CompareAndSet provides a mock function with given fields: keyVal, oldValue
func (_m *PluginStore) CompareAndSet(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) {
ret := _m.Called(keyVal, oldValue)
var r0 bool
if rf, ok := ret.Get(0).(func(*model.PluginKeyValue, []byte) bool); ok {
r0 = rf(keyVal, oldValue)
} else {
r0 = ret.Get(0).(bool)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(*model.PluginKeyValue, []byte) *model.AppError); ok {
r1 = rf(keyVal, oldValue)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// Delete provides a mock function with given fields: pluginId, key
func (_m *PluginStore) Delete(pluginId string, key string) store.StoreChannel {
ret := _m.Called(pluginId, key)