稍微提几个可改进之处 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
keakon
V2EX    Project Babel

稍微提几个可改进之处

  •  
  •   keakon 2010-11-17 16:57:45 +08:00 5890 次点击
    这是一个创建于 5443 天前的主题,其中的信息可能已经有所发展或是发生改变。
    粗读了下Babel的源码,发现了一些问题。不过我说话比较直,意见可能会很难听。

    首先是TopicHitHandler和PageHitHandler这2个用于计的handler没有做事务处理。
    如果一个主题同时被多个人浏览的话,会很容易冲突。而如果此时这个主题进行了其他修改,这些修改也很可能丢失。
    至于其他修改的地方,我就没去找了,至少last_reply_by、last_modified这些字段就是为修改存在的。

    其次是很多地方的性能可以做优化。
    最明显的是很多模型的num字段实际上可以用key id来代替,get_by_id比query将近快1个数量级。
    此外还有很多索引是可以去掉的,例如Member.password我就想不出要index的理由。
    另外,GqlQuery的解析速度是很慢的(比Query慢20倍,还得小心被GQL注入),通常都会使用bind来重用的。

    最后是代码风格。
    我觉得model和它的操作应该写在一起,作为方法或类方法。但是Babel却将其分散在各处,有的是方法,有的是v2ex.babel.da里的函数,有的是直接写在controller中。
    这种写法当然也是OK的,不过不知道Livid是否觉得维护起来很麻烦。如果新增或修改的功能涉及到model的变化,不得不到处搜索这些model、model name的usage,甚至memcache key。

    而且由于代码的分散,可重用性也因而降低,因此Handler显得过于冗长,很难一眼看出做了哪些操作。
    例如HomeHandler.get,恐怕没人能在30秒内看懂它做了哪些事然而读完却发现所做之事实际上是很简单的。
    至少在区分是否手机时,取数和渲染逻辑是大致一样的,很显然只要把memcache key和template file(或者直接用browser['ios'])作为参数就能提取出一个函数了。


    意见就说这么多,总体来说Project Babel给我的感觉更像是一个从PHP以最快方式(或者说最小努力)移植到GAE/Python的项目,而不是针对GAE去设计的项目。
    我觉得Livid应该在适当的时机尽早重构代码,不然随着功能的增多,维护势必会越来越难。
    23 条回复    1970-01-01 08:00:00 +08:00
    Livid
        1
    Livid  
    MOD
    PRO
       2010-11-17 17:18:54 +08:00
    关于 .key().id() 的问题,可能是因为我还没有完全理解及实验充分。但是我看到的问题是:

    1. 这个东西貌似不是加一递增的?

    2. 可以自己指定么?

    保留传统的自增 id 是 V2EX 在规划时我觉得必须要有的一个功能,于是就用了 counter + num 的方式实现。
    Livid
        2
    Livid  
    MOD
    PRO
       2010-11-17 17:22:07 +08:00
    把和 model 有关的东西写在 model 里我非常同意,这也是以后的代码方向。
    Livid
        3
    Livid  
    MOD
    PRO
       2010-11-17 17:26:11 +08:00
    HomeHandler 在 /my/ 及相关设置做好之后会重构。

    代码这样的东西,说到底是为运营服务的。

    有的时候,为了上线的时间和运营的需要,我可能更多关心的是如何让一个能跑的东西尽快 up and running,代码的美观性不是我的主要考虑因素。

    而且代码风格这样的东西,当你同时在用多种语言编程的时候,比如我现在每天都会用到 C / Objective-C / PHP / Python,有的时候确实稍不注意就会出现很奇怪的合体产物了。
    Los
        4
    Los  
       2010-11-17 17:26:26 +08:00
    嗯,建议逻辑上尽量减少if之类语句的嵌套,这样逻辑会清晰不少
    c
        5
    c  
       2010-11-17 18:21:07 +08:00
    有些东西在很久前都建议过,没有菜我,也许我太菜了吧。

    不过也无所谓,存在即为合理
    c
        6
    c  
       2010-11-17 18:22:34 +08:00
    key().id()不能自定义,
    可以用key().name()啊,这个只是稍微比key().id()慢那么一点点。
    keakon
        7
    keakon  
    OP
       2010-11-17 18:55:56 +08:00
    @Livid

    key id可以自定义,例如构造一个Topic实体,id为123:
    Topic(key=db.Key.from_path('Topic', 123))

    不过,非要以1递增不知道有什么用意,似乎排序时不需要按id来排列,都是以时间来排序的

    不使用事务或加双重锁的话,2个人同时post 2个topic时,很可能会覆盖掉其中一个
    c
        8
    c  
       2010-11-17 19:04:13 +08:00
    @keakon 我又去官方看了下文档,发现key().name()和key().id()基本一样?
    Livid
        9
    Livid  
    MOD
    PRO
       2010-11-17 19:05:54 +08:00
    @keakon 理论上来说确实是这样的。但是目前这样的情况还没有发生过。
    Livid
        10
    Livid  
    MOD
    PRO
       2010-11-17 19:07:47 +08:00
    @c 所以我对你开始做自己的论坛程序这件事情,感到非常赞赏。做比说更有价值。

    欢迎大家关注 @c 的作品:

    http://xfox.appspot.com/

    我暂时还没有时间去看 @c 的代码,不过我相信他肯定比我写得好。
    c
        11
    c  
       2010-11-17 19:17:23 +08:00
    @Livid xFox不会再继续开发了,因为有些问题解决不了 :) 所以现在把2008年的一个相册拿出来重写了一下。

    我不认为我的代码比你的好,我只是认为你的代码本来可以写的更优雅一点,因为将来会有很多人看你的代码。
    keakon
        12
    keakon  
    OP
       2010-11-17 19:19:32 +08:00
    @c 不一样,你用get_by_id()和get_by_key_name()取一下就会发现,1和'1'都可以存在,并且表示的是2个不同的实体。

    此外用key_name构造的key会比id要大,且随key_name长度而增长,而id是定长的int64。

    实际上我习惯用key_name来减少一个唯一字段,找不到这种字段时,我才会用id

    例如用户类,id对我来说没有任何作用,那么我就会拿用户名或email这种唯一字段来做key_name;而文章类虽说可以强制要求URL唯一,但URL是有可能去改动的,所以仍然只能选择id
    darasion
        13
    darasion  
       2010-11-17 19:19:49 +08:00
    话说,事务处理这个地方我一直就没弄明白。

    到底谁和谁一起可以做一个“Entity Group”? 这个我总是搞不懂。。。
    Livid
        14
    Livid  
    MOD
    PRO
       2010-11-17 19:21:31 +08:00
    @keakon 个人页面上的“V2EX 的第 # 号注册会员”这个功能,在最早那个版本的 V2EX 上就有,所以这次在 GAE 上重建时,我当然希望尽可能复现以前所有的要点。
    c
        15
    c  
       2010-11-17 19:23:06 +08:00
    @keakon 因为我喜欢用漂亮的slug,所以我原来一直用key_name,这两个效率应该是一样的吧?
    keakon
        16
    keakon  
    OP
       2010-11-17 19:29:46 +08:00
    只能说效率几乎是一样的,但是key_name生成的key比较大,因此实体及其索引(每个索引都包含key)也会多占用一些空间

    此外key只能get,在query时基本上没什么用处,所以如果想取key name是'1'开头的实体就很难了,至少文档里没有介绍__key__比较是否能用于这种情况
    c
        17
    c  
       2010-11-17 19:33:48 +08:00
    @keakon 我一般都会建立一个字段,比如slug来保存key_name的,这样你上面说的问题也可以解决了。

    主要是我喜欢优美的URL,现在不是要让URL有意义吗?
    keakon
        18
    keakon  
    OP
       2010-11-17 19:57:51 +08:00
    @c
    这样就不得不多占用存储空间了。而且正如前面所说,假如你的实体生成以后,突然发现slug里有错别字,不得不更改,你就只能删掉重新创建一个实体了。而如果这个实体还是根实体的话,整个实体组可能都得重新创建。

    @darasion
    在构造实体(准确来说是它的key)时可以指定一个实体为它的父实体。而且这个实体也可以作为其他实体的父实体。
    由于一个实体最多只能有1个父实体(但是可以有多个子实体),所以一直向上总能找到一个没有父实体的实体,它就是这个实体组的根实体。
    在单个事务中,你只能更改1个实体组里面的实体。

    举例来说,如果把Reply的父实体定为Topic的话,用户在post一个reply时,就能在事务中完成创建Reply实体并将它的父实体Topic的reply字段加1。
    而如果它们不在一个实体组,你就只能先put一个Reply实体,然后再找到对应的Topic实体,再给它的reply+1。而如果你Reply实体put成功,Topic实体put失败,那么就存在不一致了。
    Google给出的方式是分离事务,也就是用一个task来执行Topic的保存,因为task在失败时会自动重试,直到成功。

    更常见的例子,如果不使用事务的可能造成这种情况:
    1.一个用户a的访问使得你得给Topic的hit加1,于是你取出了这个topic。
    2.同时,另一个用户b也要编辑这个Topic的content字段,于是也取出了这个topic
    3.b编辑完了,保存成功。
    4.a的hit += 1执行完了,也进行保存,但是这个topic的content字段是a取出来时的内容,于是这次保存就让b的编辑无效了。

    而单个实体本身就是一个实体组,因此自然可以使用事务,它就可以保证2个人取出数据到保存成功这个过程是串行的,相互之间不会覆盖。
    darasion
        19
    darasion  
       2010-11-17 20:14:00 +08:00
    @keakon 谢谢解答。

    还有几个问题,

    1、如果我想在一个方法里边更新很多类( Kind ),而不是one Topic-many Reply这种简单的形式,那么这样的实体组怎样规划?有没有一些模式可以套用?

    2、在一个已经存在的,没有规划 事务/实体组 的项目中,引入 事务/实体组 后,应该如何迁移已经保存了的数据?能平滑过渡吗? (以one Topic-many Reply 为例)。
    c
        20
    c  
       2010-11-17 20:19:53 +08:00
    @keakon GAE的数据不是推荐的冗余吗,所以能通过冗余解决的问题都冗余。第二个slug不能修改的问题,这的确是个问题。
    keakon
        21
    keakon  
    OP
       2010-11-17 20:20:47 +08:00
    1. 使用task queue。一个实体保存成功后创建另一个task来更新下一个实体。文档里好像有个例子。

    2. 实体一旦保存到数据库,它的实体组关系就不能变更了。因此你只能下载所有实体,删除所有实体,然后在上传时通过设置key的parent来构造实体组。
    c
        22
    c  
       2010-11-17 20:27:57 +08:00
    darasion
        23
    darasion  
       2010-11-17 21:07:01 +08:00
    谢 @keakon @c 二位。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3445 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 04:50 PVG 12:50 LAX 21:50 JFK 00:50
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86