diff options
Diffstat (limited to 'src/poc.erl')
| -rw-r--r-- | src/poc.erl | 167 |
1 files changed, 0 insertions, 167 deletions
diff --git a/src/poc.erl b/src/poc.erl deleted file mode 100644 index cee0965..0000000 --- a/src/poc.erl +++ /dev/null @@ -1,167 +0,0 @@ --module(poc). --export([test/0, setup/0]). - -test() -> - io:format("~nVolcano Iterator~n~n"), - %% Setup in-memory "database" (simulating Mnesia) - io:format("Setting up content-addressed storage...~n"), - Storage = setup(), - %% Insert data with automatic deduplication - io:format("Inserting products (note 'Soap' is stored only once)...~n"), - {Storage2, SoapHash} = store_value(Storage, "Soap"), - {Storage3, Price1Hash} = store_value(Storage2, 1.0), - {Storage4, BazookaHash} = store_value(Storage3, "Bazooka"), - {Storage5, Price2Hash} = store_value(Storage4, 413.0), - {Storage6, _SoapHash2} = store_value(Storage5, "Soap"), % Duplicate on purpose - {Storage7, Price3Hash} = store_value(Storage6, 50.0), - io:format("Unique values stored: ~p~n", [maps:size(maps:get(values, Storage7))]), - io:format("(Expected: 5, not 6 becase 'Soap' got deduplicated)~n~n"), - - Tuple1 = #{name => SoapHash, price => Price1Hash}, - Tuple2 = #{name => BazookaHash, price => Price2Hash}, - Tuple3 = #{name => SoapHash, price => Price3Hash}, - Tuples = [Tuple1, Tuple2, Tuple3], - - io:format("Created 3 product tuples (referencing hashes)~n"), - io:format("Tuple1: #{name => ~s, price => ~s}~n", - [short_hash(SoapHash), short_hash(Price1Hash)]), - io:format("Tuple2: #{name => ~s, price => ~s}~n", - [short_hash(BazookaHash), short_hash(Price2Hash)]), - io:format("Tuple3: #{name => ~s, price => ~s}~n~n", - [short_hash(SoapHash), short_hash(Price3Hash)]), - - io:format("Building query pipeline (Volcano Model):~n"), - io:format("Scan -> SELECT(price =< 100) -> Project(name)~n~n"), - - ScanPid = spawn(fun() -> scan_iterator(Tuples, Storage7) end), - - SelectPid = spawn(fun() -> - select_iterator(ScanPid, Storage7, fun(#{price := P}) -> P =< 100.0 end) - end), - - ProjectPid = spawn(fun() -> - project_iterator(SelectPid, [name]) - end), - - io:format("Pulling results (on-demand, lazy evaluation):~n"), - Results = pull_all(ProjectPid), - - io:format("~n"), - lists:foreach(fun(#{name := Name}) -> - io:format("-> #{name => ~p}~n", [Name]) - end, Results), - ok. - -setup() -> - #{values => #{}}. % Hash => Value map - -store_value(Storage, Value) -> - Hash = value_hash(Value), - Values = maps:get(values, Storage), - case maps:is_key(Hash, Values) of - true -> - io:format("[DEDUP] Value ~p already exists (hash: ~s)~n", - [Value, short_hash(Hash)]), - {Storage, Hash}; - false -> - io:format("[STORE] Value ~p (hash: ~s)~n", - [Value, short_hash(Hash)]), - NewValues = maps:put(Hash, Value, Values), - {Storage#{values => NewValues}, Hash} - end. - -get_value(Storage, Hash) -> - Values = maps:get(values, Storage), - maps:get(Hash, Values). - -value_hash(Value) -> - %% Simple hash for demo (in production: crypto:hash(sha256, term_to_binary(Value))) - erlang:phash2(Value). - -short_hash(Hash) -> - lists:flatten(io_lib:format("~8.16.0B", [Hash])). - -scan_iterator([], _Storage) -> - receive - {next, Caller} -> Caller ! done - end; -scan_iterator([TupleWithHashes | Rest], Storage) -> - receive - {next, Caller} -> - %% Unpacks all the hashes from each tuple - %% later we gotta use this on the merkle tree - %% for now it's just silly and inneficient - ResolvedTuple = maps:map(fun(_Attr, Hash) -> - get_value(Storage, Hash) - end, TupleWithHashes), - io:format("[SCAN] Emitting tuple: ~p~n", [ResolvedTuple]), - Caller ! {tuple, ResolvedTuple}, - scan_iterator(Rest, Storage) - end. - -select_iterator(SourcePid, Storage, Predicate) -> - receive - {next, Caller} -> - SourcePid ! {next, self()}, - receive - done -> - Caller ! done; - {tuple, Tuple} -> - case Predicate(Tuple) of - true -> - io:format("[SELECT] Pass: ~p~n", [Tuple]), - Caller ! {tuple, Tuple}, - select_iterator(SourcePid, Storage, Predicate); - false -> - io:format("[SELECT] Reject: ~p~n", [Tuple]), - %% Ignore this dude, and lock the process again - select_iterator_loop(SourcePid, Storage, Predicate, Caller) - end - end - end. - -select_iterator_loop(SourcePid, Storage, Predicate, Caller) -> - SourcePid ! {next, self()}, - receive - done -> - Caller ! done; - {tuple, Tuple} -> - case Predicate(Tuple) of - true -> - io:format("[SELECT] Pass: ~p~n", [Tuple]), - Caller ! {tuple, Tuple}, - select_iterator(SourcePid, Storage, Predicate); - false -> - io:format("[SELECT] Reject: ~p~n", [Tuple]), - select_iterator_loop(SourcePid, Storage, Predicate, Caller) - end - end. - -project_iterator(SourcePid, Attributes) -> - receive - {next, Caller} -> - SourcePid ! {next, self()}, - receive - done -> - Caller ! done; - {tuple, Tuple} -> - ProjectedTuple = maps:with(Attributes, Tuple), - io:format("[PROJECT] ~p -> ~p~n", [Tuple, ProjectedTuple]), - Caller ! {tuple, ProjectedTuple}, - project_iterator(SourcePid, Attributes) - end - end. - -pull_all(IteratorPid) -> - pull_all(IteratorPid, []). - -pull_all(IteratorPid, Acc) -> - IteratorPid ! {next, self()}, - receive - done -> - lists:reverse(Acc); - {tuple, Tuple} -> - pull_all(IteratorPid, [Tuple | Acc]) - after 1000 -> - {error, timeout} - end. |
