This is a new revision of the discovery server. Relevant changes and non-changes: - Protocol towards clients is unchanged. - Recommended large scale design is still to be deployed nehind nginx (I tested, and it's still a lot faster at terminating TLS). - Database backend is leveldb again, only. It scales enough, is easy to setup, and we don't need any backend to take care of. - Server supports replication. This is a simple TCP channel - protect it with a firewall when deploying over the internet. (We deploy this within the same datacenter, and with firewall.) Any incoming client announces are sent over the replication channel(s) to other peer discosrvs. Incoming replication changes are applied to the database as if they came from clients, but without the TLS/certificate overhead. - Metrics are exposed using the prometheus library, when enabled. - The database values and replication protocol is protobuf, because JSON was quite CPU intensive when I tried that and benchmarked it. - The "Retry-After" value for failed lookups gets slowly increased from a default of 120 seconds, by 5 seconds for each failed lookup, independently by each discosrv. This lowers the query load over time for clients that are never seen. The Retry-After maxes out at 3600 after a couple of weeks of this increase. The number of failed lookups is stored in the database, now and then (avoiding making each lookup a database put). All in all this means clients can be pointed towards a cluster using just multiple A / AAAA records to gain both load sharing and redundancy (if one is down, clients will talk to the remaining ones). GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4648
122 lines
3.1 KiB
Go
122 lines
3.1 KiB
Go
package toml
|
|
|
|
import "strings"
|
|
|
|
// MetaData allows access to meta information about TOML data that may not
|
|
// be inferrable via reflection. In particular, whether a key has been defined
|
|
// and the TOML type of a key.
|
|
type MetaData struct {
|
|
mapping map[string]interface{}
|
|
types map[string]tomlType
|
|
keys []Key
|
|
decoded map[string]bool
|
|
context Key // Used only during decoding.
|
|
}
|
|
|
|
// IsDefined returns true if the key given exists in the TOML data. The key
|
|
// should be specified hierarchially. e.g.,
|
|
//
|
|
// // access the TOML key 'a.b.c'
|
|
// IsDefined("a", "b", "c")
|
|
//
|
|
// IsDefined will return false if an empty key given. Keys are case sensitive.
|
|
func (md *MetaData) IsDefined(key ...string) bool {
|
|
if len(key) == 0 {
|
|
return false
|
|
}
|
|
|
|
var hash map[string]interface{}
|
|
var ok bool
|
|
var hashOrVal interface{} = md.mapping
|
|
for _, k := range key {
|
|
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
|
return false
|
|
}
|
|
if hashOrVal, ok = hash[k]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Type returns a string representation of the type of the key specified.
|
|
//
|
|
// Type will return the empty string if given an empty key or a key that
|
|
// does not exist. Keys are case sensitive.
|
|
func (md *MetaData) Type(key ...string) string {
|
|
fullkey := strings.Join(key, ".")
|
|
if typ, ok := md.types[fullkey]; ok {
|
|
return typ.typeString()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
|
|
// to get values of this type.
|
|
type Key []string
|
|
|
|
func (k Key) String() string {
|
|
return strings.Join(k, ".")
|
|
}
|
|
|
|
func (k Key) maybeQuotedAll() string {
|
|
var ss []string
|
|
for i := range k {
|
|
ss = append(ss, k.maybeQuoted(i))
|
|
}
|
|
return strings.Join(ss, ".")
|
|
}
|
|
|
|
func (k Key) maybeQuoted(i int) string {
|
|
quote := false
|
|
for _, c := range k[i] {
|
|
if !isBareKeyChar(c) {
|
|
quote = true
|
|
break
|
|
}
|
|
}
|
|
if quote {
|
|
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
|
|
}
|
|
return k[i]
|
|
}
|
|
|
|
func (k Key) add(piece string) Key {
|
|
newKey := make(Key, len(k)+1)
|
|
copy(newKey, k)
|
|
newKey[len(k)] = piece
|
|
return newKey
|
|
}
|
|
|
|
// Keys returns a slice of every key in the TOML data, including key groups.
|
|
// Each key is itself a slice, where the first element is the top of the
|
|
// hierarchy and the last is the most specific.
|
|
//
|
|
// The list will have the same order as the keys appeared in the TOML data.
|
|
//
|
|
// All keys returned are non-empty.
|
|
func (md *MetaData) Keys() []Key {
|
|
return md.keys
|
|
}
|
|
|
|
// Undecoded returns all keys that have not been decoded in the order in which
|
|
// they appear in the original TOML document.
|
|
//
|
|
// This includes keys that haven't been decoded because of a Primitive value.
|
|
// Once the Primitive value is decoded, the keys will be considered decoded.
|
|
//
|
|
// Also note that decoding into an empty interface will result in no decoding,
|
|
// and so no keys will be considered decoded.
|
|
//
|
|
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
|
// that do not have a concrete type in your representation.
|
|
func (md *MetaData) Undecoded() []Key {
|
|
undecoded := make([]Key, 0, len(md.keys))
|
|
for _, key := range md.keys {
|
|
if !md.decoded[key.String()] {
|
|
undecoded = append(undecoded, key)
|
|
}
|
|
}
|
|
return undecoded
|
|
}
|