Go concurrency considered harmful

Concurrency At Work: The Map

Sharing by Communicating is a Lie

type ConcurrentDict interface {
// SetKey sets the key to val, val no matter if it exists or not
SetKey(key, val string)
// CasVal performs an atomic-compare-and-swap operation for a given key. setOnNotExists overwrites the "nil" value
CasVal(key, oldVal, newVal string, setOnNotExists bool) bool
// ReadKey returns the given value associated with a key, and if it exists in the map or not.
ReadKey(key string) (string, bool)
// DeleteKey removes a key from the map. If the key does not exist, we perform the operation without error
DeleteKey(key string)
}
Leakin’ Goroutines.
func TestBreak(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
d := NewChanDict(ctx)
d.ReadKey("foo")
/* Simulate upstream request failure */
cancel()
d.ReadKey("foo")
}

But SyncMap?

My Erlang Dictionary

  1. Data is always passed by value, never by reference: Every time there is communication between two processes in Erlang, the data is copied from one process to the other. This is in part because Erlang does not have pointers.
  2. Processes have unique identifiers: Each process in Erlang gets a pid, which uniquely identifies the process, during its lifetime, and the node which it is running on. There are mechanisms in Erlang called monitors, and links that allow for explicit relationships between processes. This allows the developer to say a process is dependent on on another, and their fates should be shared. There’s much more nuance that’s covered in “Learn You Some Erlang
  3. Each Process has its own private heap. This means that there is no way for concurrent manipulation of data without communicating.
%% API
-export([
set_key/3,
read_key/2,
delete_key/2,
cas_val/5
]).

-type my_dict() :: pid().
-type key() :: term().
-type value() :: term().

-export_type([my_dict/0, key/0, value/0]).

-type state() :: #{}.

%%%=================================================================
%%% API
%%%=================================================================
-spec(set_key(MyDict :: my_dict(), Key :: key(), Value :: value()) -> ok).
set_key(MyDict, Key, Value) ->
gen_server:call(MyDict, {set_key, Key, Value}).

-spec(read_key(MyDict :: my_dict(), Key :: key()) -> {ok, value()} | not_found).
read_key(MyDict, Key) ->
gen_server:call(MyDict, {read_key, Key}).

-spec(delete_key(MyDict :: my_dict(), Key :: key()) -> ok).
delete_key(MyDict, Key) ->
gen_server:call(MyDict, {delete_key, Key}).

-spec(cas_val(MyDict :: my_dict(), Key :: key(), OldVal :: value(), NewValue :: value(), SetOnNotExists :: boolean()) -> boolean()).
cas_val(MyDict, Key, OldVal, NewVal, SetOnNotExists) ->
gen_server:call(MyDict, {cas_val, Key, OldVal, NewVal, SetOnNotExists}).
%%%================================================================
%%% Internal functions
%%%================================================================

-spec(handle_set_key(Key :: key(), Value :: value(), State0 :: state()) -> {reply, ok, state()}).
handle_set_key(Key, Value, State0) ->
{reply, ok, maps:put(Key, Value, State0)}.

-spec(handle_read_key(Key :: key(), State0 :: state()) -> {reply, {ok, value()} | not_found, state()}).
handle_read_key(Key, State0) ->
case maps:find(Key, State0) of
{ok, Value} ->
{reply, {ok, Value}, State0};
error ->
{reply, not_found, State0}
end.

-spec(handle_delete_key(Key :: key(), State0 :: state()) -> {reply, ok, state()}).
handle_delete_key(Key, State0) ->
{reply, ok, maps:remove(Key, State0)}.

-spec(handle_cas_val(Key :: key(), OldVal :: value(), NewVal :: value(), SetOnNotExists :: boolean(), State0 :: state()) -> {reply, boolean(), state()}).
handle_cas_val(Key, OldVal, NewVal, SetOnNotExists, State0) ->
case maps:find(Key, State0) of
{ok, OldVal} ->
{reply, true, maps:put(Key, NewVal, State0)};
error when SetOnNotExists == true ->
{reply, true, maps:put(Key, NewVal, State0)};
_ ->
{reply, false, State0}
end.

Does it Leak? Where’s the GC?

Where does this take us?

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Create a Simple search in your website use Laravel?

Controlling PowerPoint w/ Python

Cracking the Coding Interview Review

Release Notes — March 26

Outreachy internship with Mozilla: API Gateway

Configuring SimpleSAMLphp for Single-Sign On with SalesForce

xampp control panel

What is Firebase?

HOW TO CREATE KEY-PAIR, SECURITY GROUP, EBS VOLUME AND HOW TO LAUNCH INSTANCE IN AWS CLOUD USING…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sargun Dhillon

Sargun Dhillon

More from Medium

Go bits: Interfaces and Nil Pointers

JSON Vs Protobuf — Which is the best way to store data in the cache (Redis).

Golang packages

The Difference between Arrays and Slices in Golang