[
https://issues.apache.org/jira/browse/COUCHDB-769?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14956756#comment-14956756
]
ASF GitHub Bot commented on COUCHDB-769:
----------------------------------------
Github user kxepal commented on a diff in the pull request:
https://github.com/apache/couchdb-fabric/pull/33#discussion_r41983028
--- Diff: src/fabric_attachments_handler.erl ---
@@ -0,0 +1,333 @@
+%% @author gilv
+%% @doc @todo Add description to fabric_swift_handler.
+
+
+-module(fabric_attachments_handler).
+
+-include_lib("fabric/include/fabric.hrl").
+-include_lib("couch/include/couch_db.hrl").
+
+-export([inline_att_store/2, inline_att_handler/3, container_handler/2,
normal_att_store/5, externalize_att/1]).
+
+
+%% ====================================================================
+%% External API functions. I try to keep them as general as possible,
+%% without correlation to specific object store that might be internally
used.
+%% ====================================================================
+
+normal_att_store(FileName,Db,ContentLen,MimeType,Req) ->
+ ContainerName = container_name(Db),
+ couch_log:debug("Standard attachment handler",[]),
+ couch_log:debug("Going to store ~p of length is ~p, ~p in the
container: ~n",[FileName,ContentLen,ContainerName]),
+ %Bad implementation - no chunk reader. All kept in the memory. Should
be fixed.
+ Data = couch_httpd:recv(Req, ContentLen),
+ case swift_put_object(ContainerName, FileName, MimeType, [], Data) of
+ {ok,{201,NewUrl}} ->
+ couch_log:debug("Created. Swift response code is 201 ~n",[]),
+ {ObjectSize,EtagMD5} = swift_head_object(ContainerName,
FileName),
+ {unicode:characters_to_binary(NewUrl, unicode, utf8),
ObjectSize, EtagMD5};
+ {_,{Code,_}} ->
+ couch_log:debug("Swift response code is ~p~n",[]),
+ {Data, -1}
+ end.
+
+inline_att_store(Db, Docs) ->
+ DbName = container_name(Db),
+ couch_log:debug("Store inline base64 encoded attachment ~n",[]),
+ if
+ is_list(Docs) ->
+ couch_log:debug("Going to handle document list",[]),
+ DocsArray = Docs;
+ true ->
+ couch_log:debug("Going to handle single document",[]),
+ DocsArray = [Docs]
+ end,
+ [#doc{atts=Atts0} = Doc | Rest] = DocsArray,
+ if
+ length(Atts0) > 0 ->
+ couch_log:debug("Att length is larger than 0",[]),
+ Atts = [att_processor(DbName,Att) || Att <- Atts0],
+ if
+ is_list(Docs) ->
+ [Doc#doc{atts = Atts} | Rest];
+ true ->
+ Doc#doc{atts = Atts}
+ end;
+ true ->
+ couch_log:debug("No attachments to handle",[]),
+ Docs
+ end.
+
+inline_att_handler(get, Db, Att) ->
+ DbName = container_name(Db),
+ couch_log:debug("Retrieve attachment",[]),
+ [Data,Name,AttLen,AttLenUser] = couch_att:fetch([data, name,att_len,
att_external_size],Att),
+ couch_log:debug("Going to retrieve ~p. DB length is: ~p, stored
length: ~p~n",[Name, AttLen, AttLenUser]),
+ NewData = swift_get_object(DbName, Name),
+ NewAtt = couch_att:store([{data,
NewData},{att_len,AttLenUser},{disk_len,AttLenUser}], Att),
+ NewAtt;
+
+inline_att_handler(delete,Db, Att) ->
+ DbName = container_name(Db),
+ couch_log:debug("Delete attachment ~p~n",[]).
+
+container_handler(create,DbName) ->
+ couch_log:debug("Create container ~p~n",[DbName]),
+ case swift_create_container(DbName) of
+ {ok,{201,Container}} ->
+ couch_log:debug("Container ~p created succesfully
~n",[Container]),
+ {ok,Container};
+ {error,_} ->
+ couch_log:debug("Container ~p creation failed ~n",[DbName]),
+ {error,DbName}
+ end;
+container_handler(get,DbName) ->
+ couch_log:debug("Get container ~p~n",[DbName]);
+container_handler(delete,DbName)->
+ couch_log:debug("Delete container ~p~n",[DbName]),
+ swift_delete_container(DbName).
+
+externalize_att(Db) ->
+ Res = config:get("swift","attachments_offload","false"),
+ Res.
+
+%% ====================================================================
+%% Internal general functions
+%% ====================================================================
+
+container_name(Db) ->
+ Suffix = list_to_binary(mem3:shard_suffix(Db)),
+ DbNameSuffix = erlang:iolist_to_binary([fabric:dbname(Db), Suffix]),
+ couch_log:debug("Db to Container name ~p~n",[DbNameSuffix]),
+ DbNameSuffix.
+
+hex_to_bin(Str) -> << << (erlang:list_to_integer([H], 16)):4 >> || H <-
Str >>.
+
+%% ====================================================================
+%% Internal OpenStack Swift implementation
+%% ====================================================================
+
+extractAuthInfo([{_,_}|_] = Obj,[])->
+ Storage_URL = proplists:get_value(<<"publicURL">>, Obj),
+ Storage_Token = proplists:get_value(<<"id">>, Obj),
+ Status = true,
+ [Storage_URL, Storage_Token, Status];
+extractAuthInfo([{[{_,_}|_]}] = Obj,[])->
+ [{ObjNext}] = Obj,
+ Storage_URL = proplists:get_value(<<"publicURL">>, ObjNext),
+ Storage_Token = proplists:get_value(<<"id">>, ObjNext),
+ Status = true,
+ [Storage_URL, Storage_Token, Status];
+extractAuthInfo([{_,_}|L]=Obj,KeyValPath)->
+ [{Key, Val}|KeyValPathNext] = KeyValPath,
+ ObjVal = proplists:get_value(Key,Obj),
+ case Val of
+ [] -> extractAuthInfo(ObjVal, KeyValPathNext);
+ ObjVal -> extractAuthInfo(Obj, KeyValPathNext);
+ _ -> ["", "", false]
+ end;
+extractAuthInfo([{[{_,_}|_]}|L]=Obj,KeyValPath)->
+ [ObjCur| _] = Obj,
+ [Storage_URL, Storage_Token, Status] = extractAuthInfo(ObjCur,
KeyValPath),
+ case Status of
+ false -> extractAuthInfo(L, KeyValPath);
+ _ -> [Storage_URL, Storage_Token, Status]
+ end;
+extractAuthInfo(Obj,KeyValPath) when is_tuple(Obj)->
+ {Doc} = Obj,
+ extractAuthInfo(Doc, KeyValPath).
+
+att_processor(DbName,Att) ->
+ couch_log:debug("Swift: attachment processor",[]),
+ [Type, Enc, DiskLen, AttLen] = couch_att:fetch([type, encoding,
disk_len, att_len], Att),
+ [Name, Data] = couch_att:fetch([name, data], Att),
+ couch_log:debug("Swift: att name: ~p, type: ~p, encoding: ~p, disk
len: ~p~n",[Name,Type,Enc,DiskLen]),
+ case is_binary(Data) of
+ true ->
+ couch_log:debug("Swift: binary attachment exists",[]),
+ case swift_put_object(DbName, Name, Type, [], Data) of
+ {ok,{201,NewUrl}} ->
+ N1 = unicode:characters_to_binary(NewUrl, unicode,
utf8),
+ couch_log:debug("~p ~p~n",[N1, NewUrl]),
+ NewAtt = couch_att:store(data,N1,Att),
+ couch_log:debug("Swift. testing store in original
length ~p~n",[AttLen]),
+ {ObjectSize,EtagMD5} = swift_head_object(DbName, Name),
+ NewAtt1 =
couch_att:store([{att_external_size,ObjectSize},{att_external,"external"},{att_external_md5,EtagMD5}],NewAtt),
+ NewAtt1;
+ {_,{Code,_}} ->
+ couch_log:debug("Swift: response code is ~p~n",[Code]),
+ Att
+ end;
+ _ ->
+ Att
+ end.
+
+swift_put_object(Container, ObjName, ContextType, CustomHeaders, Data) ->
+ couch_log:debug("Swift: PUT ~p/~s object method ~n",[Container,
ObjName]),
+ case authenticate() of
+ {ok,{Url,Storage_Token}} ->
+ ObjNameEncoded = couch_util:url_encode(ObjName),
+ Headers = CustomHeaders ++ [{"X-Auth-Token",Storage_Token}],
+ Method = put,
+ couch_log:debug("Swift: going to upload : ~p ~p ~p ~p
~p~n",[Url, Headers, ContextType,ObjNameEncoded, Container]),
+ NewUrl = Url ++ "/" ++ unicode:characters_to_list(Container)
++ "/" ++ unicode:characters_to_list(ObjNameEncoded),
+ couch_log:debug("Swift: url: ~p~n",[NewUrl]),
+ R = httpc:request(Method, {NewUrl, Headers,
unicode:characters_to_list(ContextType), Data}, [], []),
+ couch_log:debug("~p~n",[R]),
+ case R of
+ {ok,_} ->
+ {ok, {{"HTTP/1.1",ReturnCode, _}, _, _}} = R,
+ {ok,{ReturnCode,NewUrl}};
+ Error ->
+ {error,{element(2,Error),NewUrl}}
+ end;
+ {not_authenticated,_} ->
+ {error,"Not authenticated",""};
+ {auth_not_supported, Error} ->
+ {error,{element(2,Error),""}}
+ end.
+
+swift_delete_object(Container, ObjName) ->
+ couch_log:debug("Swift: Delete ~p/~p object method ~n",[Container,
ObjName]).
+
+swift_get_object(Container, ObjName) ->
+ couch_log:debug("Swift: get object ~p/~p ~n",[Container, ObjName]),
+ case authenticate() of
+ {ok,{Url,Storage_Token}} ->
+ Header = [{"X-Auth-Token",Storage_Token}],
+ ObjNameEncoded = couch_util:url_encode(ObjName),
+ Method = get,
+ NewUrl = Url ++ "/" ++ unicode:characters_to_list(Container)
++ "/" ++ unicode:characters_to_list(ObjNameEncoded),
+ couch_log:debug("Swift: url ~p~n",[NewUrl]),
+ R = httpc:request(Method, {NewUrl, Header}, [], []),
+ {ok, {{"HTTP/1.1",ReturnCode, _}, Head, Body}} = R,
+ couch_log:debug("Swift: ~p~p ~n",[ReturnCode, Head]),
+ Body;
+ {not_authenticated,_} ->
+ {error, "Not authenticated"};
+ {not_supported, Error} ->
+ {error,{element(2,Error),""}}
+ end.
+
+swift_head_object(Container, ObjName) ->
+ couch_log:debug("Swift: head object ~p/~p ~n",[Container, ObjName]),
+ case authenticate() of
+ {ok,{Url,Storage_Token}} ->
+ Header = [{"X-Auth-Token",Storage_Token}],
+ Method = head,
+ ObjNameEncoded = couch_util:url_encode(ObjName),
+ NewUrl = Url ++ "/" ++ unicode:characters_to_list(Container)
++ "/" ++ unicode:characters_to_list(ObjNameEncoded),
+ couch_log:debug("Swift: url ~p~n",[NewUrl]),
+ R = httpc:request(Method, {NewUrl, Header}, [], []),
+ {ok, {{"HTTP/1.1",ReturnCode, _}, Head, _}} = R,
+ couch_log:debug("Swift: ~p~p~n",[ReturnCode, Head]),
+ ObjectSize = lists:filter(fun ({"content-length",_}) -> true ;
(_) -> false end, Head),
+ EtagHeader = lists:filter(fun ({"etag",_}) -> true ; (_) ->
false end, Head),
+ Etag = element(2,lists:nth(1,EtagHeader)),
+ {ObjectSizeNumeric,_} =
string:to_integer(element(2,lists:nth(1,ObjectSize))),
+ couch_log:debug("Swift: Object size is: ~p and Etag:
~p~n",[ObjectSizeNumeric, Etag]),
+ couch_log:debug("Etag in base16 ~p~n",[Etag]),
+ EtagDecode = hex_to_bin(Etag),
+ EtagMD5 = base64:encode(EtagDecode),
+ couch_log:debug("Etag in base64 ~p~n",[EtagMD5]),
+ {ObjectSizeNumeric,EtagMD5};
+ {auth_not_supported, Error} ->
+ {error,{element(2,Error),""}};
+ {not_authenticated, _} ->
+ {-1,""}
--- End diff --
What -1 stand for?
> Store large attachments external to the .couch file
> ---------------------------------------------------
>
> Key: COUCHDB-769
> URL: https://issues.apache.org/jira/browse/COUCHDB-769
> Project: CouchDB
> Issue Type: New Feature
> Components: Database Core
> Reporter: Robert Newson
> Assignee: Adam Kocoloski
> Attachments: external_attachments_alpha.patch
>
>
> For attachment-heavy applications storing the attachments in separate files
> significantly eases compaction problems.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)