какую структуру данных можно использовать для поддержки ограничений внешних ключей - PullRequest
0 голосов
/ 12 декабря 2018

Предоставляя некоторый контекст, я реализую разветвление (потоковую передачу), используя gRPC, на сервере каждый раз, когда клиент подключается, я сохраняю поток в карту, а когда клиент отключается, я просто удаляю его из карты, чтобыЯ мог бы обновлять карту только с помощью «живых» клиентов / соединений.

Таким образом, исходная карта выглядит примерно так:

key        value
client1    stream
client2    stream
client3    stream

Проблема с этой реализацией состоит в том, что, если клиент подключаетсянесколько раз только последний клиент (использующий тот же key id) получит сообщение «широковещания», так как карта перезапишет существующий поток самым последним из подключающегося клиента, поэтому мне нужно либо использовать уникальный ключ, либочтобы найти способ сопоставления, связать клиентов и соединения.

Мне было легко просто переключить карту и использовать в качестве ключа идентификатор соединения, что-то вроде этого

conn_id   client_id
conn1     client1
conn2     client2 \
conn3     client2  > 3 connections for client ID 2
conn4     client2 /

Это работает ипомогает отправить сообщение всем подключенным клиентам, но усложняет ситуацию, если я хочу отправить сообщение определенномуclient, так как мне нужно было бы ранжировать / пройти всю карту, чтобы сопоставить значение с желаемым клиентом.

В попытке исправить это я переключил карту обратно, используя client_id, но вместо того, чтобы использовать поток какзначение, я использую список срезов

key        value
client1   [stream, stream, stream]
client2   [stream]
client3   [stream, stream] 

Проблема не устранена, поскольку для отправки сообщения всем клиентам по-прежнему необходимо выполнить обход / диапазон для получения потоков для каждого клиента и отправкисообщение каждому.

Поэтому, не используя базу данных, задаюсь вопросом, какую структуру данных можно использовать, следуя рекомендациям относительно big O нотации ?

В основном хотелось бы найтичто-то похожее на базу данных внешние ключи , возможно, с двумя картами, одна из которых сохраняет все подключения, а другая - только состояние или количество подключений на клиента, например:

key      value            client_id   # streams 
conn1    client1          client1     1   
conn2    client2          client2     3
conn3    client2
conn4    client2

Но как«атомарно» удалить клиента из карты clients, когда больше нет соединений с этим клиентом в connectionsmap.

Это попытка, но интересно, можно ли ее улучшить:

https://play.golang.org/p/VEoLsWh6dbJ

package main

import (
    "fmt"
    "sync"
)

func main() {

    clients := &sync.Map{}
    for i := 0; i < 100; i++ {
        clientID := fmt.Sprintf("client-%d", i%5)
        connID := fmt.Sprintf("conn-%d", i)
        client, ok := clients.LoadOrStore(clientID, connID)
        if ok {
            switch c := client.(type) {
            case string:
                client = []string{c, connID}
            case []string:
                client = append(client.([]string), connID)
            }
            clients.Store(clientID, client)
        }
    }

    list := func(k, v interface{}) bool {
        fmt.Printf("k: %+v v: %v\n", k, v)
        return true
    }

    clients.Range(list)

    // Get all connections
    broadcast := func(k, v interface{}) bool {
        fmt.Printf("To all connections from= %q\n", k)
        for _, v := range v.([]string) {
            fmt.Printf("msg to conn = %s\n", v)
        }
        return true
    }
    clients.Range(broadcast)
}

Здесь я пытаюсь использовать только карты (легко обрабатывать удаления):медленнее, но выделяет меньше байтов:

https://play.golang.org/p/hz5uRX-VAvH

package main

import (
    "fmt"
    "sync"
)

func main() {
    clients := &sync.Map{}
    for i := 0; i < 100; i++ {
        clientID := fmt.Sprintf("client-%d", i%5)
        connID := fmt.Sprintf("conn-%d", i)
        conns, ok := clients.Load(clientID)
        if ok {
            conns.(*sync.Map).Store(connID, i)
        } else {
            conns := &sync.Map{}
            conns.Store(connID, i)
            clients.Store(clientID, conns)
        }
    }

    list := func(k, v interface{}) bool {
        fmt.Printf("k: %v\n", k)
        listValue := func(j, l interface{}) bool {
            fmt.Printf("  j = %+v\n", j)
            return true
        }
        v.(*sync.Map).Range(listValue)
        return true
    }

    clients.Range(list)

    // Get all connections
    broadcast := func(k, v interface{}) bool {
        fmt.Printf("To all connections from= %q\n", k)
        listValue := func(j, l interface{}) bool {
            fmt.Printf("  msg to conn = %s -- %v\n", j, l)
            return true
        }
        v.(*sync.Map).Range(listValue)
        return true
    }
    clients.Range(broadcast)
}
...