Commits

Anonymous committed e7058b5

store X-Riak-Last-Modified as erlange:now() tuple, not rfc1123 date string

- allows for less computationally-expensive comparisons
- adds millisecond resolution

Comments (0)

Files changed (5)

src/jiak_object.erl

                        Links),
     {struct, [{<<"vclock">>,vclock_to_headers(riak_object:vclock(RiakObject))},
               {<<"lastmod">>,
-               list_to_binary(dict:fetch(<<"X-Riak-Last-Modified">>, MD))},
+               list_to_binary(
+                 case dict:fetch(<<"X-Riak-Last-Modified">>, MD) of
+                     Now={_,_,_} ->
+                         httpd_util:rfc1123_date(
+                           calendar:now_to_local_time(Now));
+                     Rfc1123 when is_list(Rfc1123) ->
+                         Rfc1123
+                 end)},
               {<<"vtag">>,
                list_to_binary(dict:fetch(<<"X-Riak-VTag">>, MD))}
               |J0]}.

src/raw_http_resource.erl

      end,
      LHead,": ",Links,"\n",
      "Etag: ",dict:fetch(?MD_VTAG, MD),"\n",
-     "Last-Modified: ",dict:fetch(?MD_LASTMOD, MD),"\n",
+     "Last-Modified: ",
+     case dict:fetch(?MD_LASTMOD, MD) of
+         Now={_,_,_} ->
+             httpd_util:rfc1123_date(
+               calendar:now_to_local_time(Now));
+         Rfc1123 when is_list(Rfc1123) ->
+             Rfc1123
+     end,
+     "\n",
      "\n",V].
     
 
 last_modified(RD, Ctx) ->
     case select_doc(Ctx) of
         {MD, _} ->
-            {httpd_util:convert_request_date(
-               dict:fetch(?MD_LASTMOD, MD)), RD, Ctx};
+            {case dict:fetch(?MD_LASTMOD, MD) of
+                 Now={_,_,_} ->
+                     calendar:now_to_universal_time(Now);
+                 Rfc1123 when is_list(Rfc1123) ->
+                     httpd_util:convert_request_date(Rfc1123)
+             end,
+             RD, Ctx};
         multiple_choices ->
             {undefined, RD, Ctx}
     end.
     %% solving this (the value can be specified per-process with spawn_opt,
     %% but this works and doesn't have a noticeable impact on performance.
     erlang:system_flag(fullsweep_after, 20),
+    confirm_epoch(),
     case riak:get_app_env(no_config) of
         true -> nop; % promising to set all env variables some other way
         _ -> riak_app:read_config()
 	{error, {already_started, App}} ->
 	    ok
     end.
+
+%% 719528 days from Jan 1, 0 to Jan 1, 1970
+%%  *86400 seconds/day
+-define(SEC_TO_EPOCH, 62167219200).
+
+%% @spec confirm_epoch() -> ok
+%% @doc 
+confirm_epoch() ->
+    %% doc for erlang:now/0 says return value is platform-dependent
+    %% -> let's emit an error if this platform doesn't think the epoch
+    %%    is Jan 1, 1970
+    {MSec, Sec, _} = erlang:now(),
+    GSec = calendar:datetime_to_gregorian_seconds(
+             calendar:universal_time()),
+    case GSec - ((MSec*1000000)+Sec) of
+        N when (N < ?SEC_TO_EPOCH+5 andalso N > ?SEC_TO_EPOCH-5);
+        (N < -?SEC_TO_EPOCH+5 andalso N > -?SEC_TO_EPOCH-5) ->
+            %% if epoch is within 10 sec of expected, accept it
+            ok;
+        N ->
+            io:format("Epoch fail:~n"),
+            Epoch = calendar:gregorian_seconds_to_datetime(N),
+            io:format("Riak expects your system's epoch to be Jan 1, 1970,~n"
+                      "but your system says the epoch is ~p~n", [Epoch]),
+            throw(epoch_fail)
+    end.

src/riak_put_fsm.erl

         false -> dict:store(<<"X-Riak-VTag">>,
                        make_vtag(RObj),
                        dict:store(<<"X-Riak-Last-Modified">>,
-                                  httpd_util:rfc1123_date(),
+                                  erlang:now(),
                                   MD0))
     end,
     riak_object:apply_updates(riak_object:update_metadata(RObj, NewMD)).

src/riak_util.erl

 moment() -> calendar:datetime_to_gregorian_seconds(calendar:universal_time()).
 
 %% @spec compare_dates(string(), string()) -> boolean()
-%% @doc Compare two RFC1123 date strings.  Return true if date A
-%%      is later than date B.
-compare_dates(A, B) ->
-    % true if A is later than B, where
-    % A and B are rfc1123 dates.
-    A1 = calendar:datetime_to_gregorian_seconds(
-	   httpd_util:convert_request_date(A)),
-    B1 = calendar:datetime_to_gregorian_seconds(
-	   httpd_util:convert_request_date(B)),
-    A1 > B1.
+%% @doc Compare two RFC1123 date strings or two now() tuples (or one
+%%      of each).  Return true if date A is later than date B.
+compare_dates(A={_,_,_}, B={_,_,_}) ->
+    %% assume 3-tuples are now() times
+    A > B;
+compare_dates(A, B) when is_list(A) ->
+    %% assume lists are rfc1123 date strings
+    compare_dates(rfc1123_to_now(A), B);
+compare_dates(A, B) when is_list(B) ->
+    compare_dates(A, rfc1123_to_now(B)).
+
+%% 719528 days from Jan 1, 0 to Jan 1, 1970
+%%  *86400 seconds/day
+-define(SEC_TO_EPOCH, 62167219200).
+
+rfc1123_to_now(String) when is_list(String) ->
+    GSec = calendar:datetime_to_gregorian_seconds(
+             httpd_util:convert_request_date(String)),
+    ESec = GSec-?SEC_TO_EPOCH,
+    Sec = ESec rem 1000000,
+    MSec = ESec div 1000000,
+    {MSec, Sec, 0}.
 
 %% @spec make_tmp_dir() -> string()
 %% @doc Create a unique directory in /tmp.  Returns the path