anchor.diff:          [] ->
               0
       end.
  +
  +log_to_file(FileName, Format, Args) ->
  +    spawn(fun()->
  +                  file:write_file(FileName, io_lib:format(Format, Args), [append])
  +          end).
  
positive.code:             %%?INFO_MSG("SkipUser:~p",[User]),
              {ok, State, 0};
          false ->
              %%?INFO_MSG("CheckUser:~p",[User]),
              {ok, State2} = transfer_roster(State, User),
              set_old_user(User),
              {ok, State2, 1}
      end.
  
  transfer_roster(#state{now_offset = NowOffset, 
                         max_offset=MaxOffset,
                         skip_cnt = SkipCnt,
                         transfer_cnt = TransferCnt}=State,User) ->
      Server = <<"easemob.com">>,
      RdsRosters = 
          case easemob_roster_cache:read_rosters(User, Server) of
              not_found ->
                  easemob_roster:get_roster(User, Server, odbc);
              Rs -> Rs
          end,
      PikaRosters =
          easemob_roster:get_roster(User, Server, pika),
      case is_list(RdsRosters) andalso is_list(PikaRosters) of
          true ->
              case is_need_transfer(RdsRosters, PikaRosters) of
                  false ->
                      ?INFO_MSG("transfer skip:~p, progress=~p/~p (~.2f%), skip_cnt=~p",
                                [User, NowOffset, MaxOffset, 100 * NowOffset / MaxOffset, SkipCnt+1]),
                      {ok, State#state{skip_cnt = SkipCnt + 1}};
                  true ->
                      TransferRes = easemob_roster_pika:write_rosters(User, RdsRosters),
                      ?INFO_MSG("transfer roster for user:~p, progress=~p/~p (~.2f%), transfer_cnt=~p, res=~p,",
                                [User, NowOffset, MaxOffset, 100 * NowOffset / MaxOffset, TransferCnt+1, TransferRes]),
                      case TransferRes  of
                          ok ->
                              {ok, State#state{transfer_cnt = TransferCnt+1}};
                          {error, Reason} ->
                              throw({transfer_roster_fail, Reason})
                      end
              end;
          false ->
              throw({read_roster_fail, RdsRosters, PikaRosters})
      end.
  
  is_need_transfer(RdsRosters, PikaRosters) when length(RdsRosters) > length(PikaRosters) ->
      true;
  is_need_transfer(_,_) ->
      false.
  
  is_old_user(User) ->
      case dets:lookup(?TAB, User) of
  
negative.code: -define(END, "'").
  -define(INFO_MSG(Format, Args), spawn(fun()->error_logger:info_msg("~s:~p "++Format++"~n",[?MODULE, ?LINE]++Args) end)).
  -define(ERROR_MSG(Format, Args), error_logger:error_msg("~s:~p "++Format++"~n",[?MODULE, ?LINE]++Args)).
  -define(LARGE_ROSTER_LENGTH, 1000).
  
  %%%===================================================================
  %%% API
  