Tailscale 的协调服务器(咱们的“控制面板”)以控制 CONTROL闻名遐迩。它目前是单个 VM 上的单个 Go 进程。它最早的原型运用的是 SQLite。咱们最初的规划与终究的规划有十分大的差异,包含同步到客户端机器上的装备数据库,以及一切咱们终究不再需求的其他概念。在这个过程中,咱们每周都要对 SQL 数据模型进行十分大规模的重组,这需求很多的键盘输入工作。SQL 已经得到了广泛运用,它耐久、有效,但将其引入到任何编程言语中简直都需求做很多的粘合。(一般咱们都试图用 ORM 来防止这种状况,用令人生厌的很多魔法字和功率损失来取代那些相同令人生厌的键盘输入工作。)
一天,我厌恶了重构,就把它彻底丢在一边,建了一个内存数据模型进行实验。这样,迭代速度更快了。几周后,一位客户想要试用一下。我还没有做好提交数据模型和用 SQL 来完成它的预备,所以我选择了一条捷径:将持有一切数据的目标包装在一个 sync.Mutex 中。一切访问都要经过它,在编辑时,将整个结构传递给 json.Marshal,然后写入磁盘。这便是咱们用大约 20 行 Go 完成的数据模型耐久层。
咱们原本方案要搬迁为其他言语的,但忙着忙着就给忘记了。
当我在规划一个包时,我感触到了编写 Go 时它的类型体系的局限性,这样的感触并不多,它是其中之一。假如我运用的是一种具有各种花哨功能的言语,那么我能够在脱离缓存的目标上放置某种 const 限定符,然后防止对内存进行克隆。即便如此,在咱们的服务器上执行的功能分析却表明,仿制并不是一个功能问题,所以该例或许说明,我实际上并不需求那些心心念念的更复杂的类型体系。一般状况下,假定很或许并不正确,功能分析才更具启发含义。