Pensieve: 2009

2020-09-25 08:49

所观所读所听

Ars Technica几个游戏主题的视频值得一看: 这儿有一个是关于Diablo的一个关于Civilization的. 实话实话, 看完后对这两位的敬意少了一点儿. Diablo本来是个回合制的游戏, 被总部建议改成即时制. David Brevik当时强烈反对这个改动, 组内投票才硬着头皮同意, 跟暴雪总部再多要一点开发资源, 结果一个晚上的时间就把这个本以为要数周的改动给完成了, 后面就转变观念觉得这是一个好主意了. 类似的, Civilization的One more turn也不是Sid一开头的主意, 这个上口的梗更多来自于玩家社区.

雪中悍刀行重读完了这是第三还是第四遍. 主要缺点是故事拖得太长主次不分, 导致最后给人戛然而止的感觉, 而且为了追求爽感, 很多时候给了一个类似漫画的画面感, 结果是本可以刻画得更细致的场景就粗粗带过了. 但是人物塑造称得上有血有肉.

所玩

Kittens Game花了我大量时间, 刷到了时间科技后觉得后面的升级还茫茫然无止境, 就放下了. 不过这个游戏的整体设计还是挺不错的, 就是坑深了点儿. 为什么大家都不愿意做那种一开始就知道几个小时可以玩穿放下, 后面会不由自主地回来玩的游戏呢?

go-git

之前说过, 我用go写了一个命令行程序来自定义命令提示符. 这个月发现它有点卡顿了, 就想办法优化了下, 对应的修改可以参考这两个commit: 240c54b548e0. 下面简要讲一下这个过程中做的尝试.

先介绍一下背景, 我之前的那个版本慢主要是卡在调用系统git命令上. 我怀疑是由于我最近升级了git客户端, 所以导致一些之前执行速度过得去的命令变慢了, 所以总体上让我感觉到了卡顿. 于是, 我们要做的最重要的事情是将这些系统命令调用改成go语言实现. 于是自然是找一个go语言原生的git库来. 我调研后看到两个伯仲之间的选项, 一个是go-git, 另一个是git2go. 前者是用go从头写的, 后者是libgit2这个C库的兼容层. 我用go-git写了一个版本出来, 前面几个命令都还好, 到最后列git status的时候, 这个库无比卡. 一个git status需要150毫秒的目录里跑这段代码需要2.5秒左右. 尝试看了下源码, 发现这个库不算太靠谱, 志愿者贡献的代码质量参差不齐, 而且git本身又是一个很考验优化能力的技术, 所以回头进了git2go的门. 优化完以后速度提示很明显: 比如, 我在我的etc目录里面跑git status, 耗时大概是40毫秒, 而我运行编译好的ps1命令的时候, 耗时大概是50毫秒, 这基本属于人感知不到的范围了.

结论: go-git坑多, 不要踩, 绕路去git2go也许是更好的选择.

StackSet

StackSet是一个大坑, 这是我深入使用这个产品两个月后的感受.

这个产品的出发点是一次部署多个Stack. 但是一般正常应用级的内容不会要求同时部署到多账户多区域: 应用一般需要部署到一个或少数几个不同环境的账号, 而且还存在发布控制的需求, 不太可能也不太适合用StackSet来部署. 因此, 这个产品实际上大部分使用场景是云账号的管理员将一些全局性, 策略性的Stack部署到部分或所有账户中的一个或多个区域里. 讲到这儿, 我们必须提一下AWS Organizations里面的另一个特性, 叫Organization Unit. 例如一个公司有若干个部门, 每个部门对于AWS的使用有不同的要求, 所以一个部门映射一个OU, 这样可以实现不同部门的账户采用不同的管理方式/策略. StackSet支持部署到一个或多个账号, 也支持部署到一个或多个OU. 好吧, 目前为止, 这一切看起来还不错.

那么现在, 除了账号和OU这两种部署方式外, 再给你加上一个自由度: StackSet的管理维护和Stack Instance的管理维护是两码事. 比如, 你可以部署完Stack Instances之后对于某一个OU或者某一些账号(取决于你的部署方式)针对某个区域删除这些Stack Instances. 这个操作会成功, 但是对维护者来说, 这个操作很难被察觉到, 因为Stack Instance没有一个一致可靠的描述字段. 又比如, 你想要把Stack A部署到OU1的悉尼区, 再部署到OU2的美国东1区, 经过这样的混合部署后, 除了一个个分析每个Stack Instance是在哪个OU哪个区域外, 你没有办法从单个API的单个字段拿到这些信息. 这样的行为对IaC很不友好, 我们期望的是能够定义一些部署目标, StackSet能够自动分析这些部署目标, 并将定义好的Stack部署到这些目标中去. 回头我们使用API查询一个StackSet状态的时候, 也能一目了然地看到这些部署目标, 心里有底.

再加上一些小坑:

所以, 我自己写代码在StackSet上面再包装了一层, 实现类似这样的API:

stack_set = StackSet(
    name="some-stack-name",
    template=template_url,
    params=params,
    tags=TAGS,
    iam=True,
)

stack_set.deploy(
    targets=[
        {"regions": [some_regions], "ouids": some_ou_ids},
        {"regions": [other_regions], "ouids": other_ou_ids},
    ],
)

这样, 我们需要在代码里面维护StackSet的部署目标, 上面的代码示例中, 我们把这个StackSet部署到some_ou_idssome_regions中去, 也要将其部署到other_ou_idsother_regions中去. 如果需要增减部署目标, 直接用这儿提供的API来完成.

simplicity is the ultimate sophistication. 为了实现这样简单的API, 我写了大概类似这样的代码:

  if not stackset.exists:
    create-stackset
  else:
    wait-for-existing-operations
    describe-stackset
    if template/parameter/tag/capabilities changes:
      update-stackset
  deploy-stack-instances

其中, deploy-stack-instances的逻辑如下:

list-stack-instances | sort by ou/region
to_add, to_del = diff stack-instances with deploy-targets
create-stack-instances(to_add)
delete-stack-instances(to_del)

有了上面这样的代码, 不管是第一次部署还是后续更新, 不管是添加还是删除部署目标, 直接改高级API上的参数就可以了.