diff --git a/CMakeLists.txt b/CMakeLists.txt
index 45b8ccb..878ef27 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required (VERSION 3.7)
 project(core
-        VERSION 0.2.12
+        VERSION 0.2.13
         LANGUAGES C)
 
 add_executable(core src/main.c)
diff --git a/include/database.h b/include/database.h
index 877e166..c892e01 100644
--- a/include/database.h
+++ b/include/database.h
@@ -24,4 +24,17 @@ database_transaction_commit();
 void
 database_transaction_rollback();
 
+
+int
+database_helper_get_id(sqlite3_stmt *stmt);
+
+int*
+database_helper_get_ids(sqlite3_stmt *stmt);
+
+char*
+database_helper_get_string(sqlite3_stmt *stmt);
+
+char**
+database_helper_get_strings(sqlite3_stmt *stmt);
+
 #endif /* CORE_DATABASE_H */
diff --git a/include/models/relay.h b/include/models/relay.h
index 9383dab..79256ea 100644
--- a/include/models/relay.h
+++ b/include/models/relay.h
@@ -56,4 +56,7 @@ relay_get_all();
 relay_t**
 relay_get_by_controller_id(int controller_id);
 
+int
+relay_get_controller_id_for_relay(int relay_id);
+
 #endif /* CORE_RELAY_H */
diff --git a/src/cache.c b/src/cache.c
index e1ec911..9b9579d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -169,11 +169,10 @@ cache_invalidate_relay(int relay_id)
     sprintf(key, "relay_json:%d", relay_id);
     cache_invalidate(key);
 
-    relay_t *relay = relay_get_by_id(relay_id);
-    if(relay)
+    int controller_id = relay_get_controller_id_for_relay(relay_id);
+    if(controller_id)
     {
-        cache_invalidate_controller(relay->controller_id);
-        relay_free(relay);
+        cache_invalidate_controller(controller_id);
     }
 }
 
diff --git a/src/database.c b/src/database.c
index 48335fb..2acaa3b 100644
--- a/src/database.c
+++ b/src/database.c
@@ -121,3 +121,158 @@ database_transaction_rollback()
     sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
     in_transaction = 0;
 }
+
+int
+database_helper_get_id(sqlite3_stmt *stmt)
+{
+    int result = 0;
+
+    for(;;)
+    {
+        int s;
+
+        s = sqlite3_step(stmt);
+        if (s == SQLITE_ROW)
+        {
+            result = sqlite3_column_int(stmt, 0);
+        }
+        else
+        {
+            if (s == SQLITE_DONE)
+            {
+                break;
+            }
+            else
+            {
+                LOGGER_ERR("error selecting id from database: %s\n", sqlite3_errstr(s));
+                sqlite3_finalize(stmt);
+                return 0;
+            }
+        }
+    }
+
+    sqlite3_finalize(stmt);
+
+    return result;
+}
+
+int*
+database_helper_get_ids(sqlite3_stmt *stmt)
+{
+    int *result = malloc(sizeof(int));
+    int new_id;
+
+    int row = 0;
+
+    for(;;)
+    {
+        int s;
+
+        s = sqlite3_step(stmt);
+        if (s == SQLITE_ROW)
+        {
+            new_id = sqlite3_column_int(stmt, 0);
+            if(new_id != 0) // found row for other target (relay <> schedule)
+            {
+                row++;
+
+                result = (int*)realloc(result, sizeof(int) * (row + 1));
+                result[row - 1] = new_id;
+            }
+        }
+        else
+        {
+            if (s == SQLITE_DONE)
+            {
+                break;
+            }
+            else
+            {
+                LOGGER_ERR("error selecting ids from database: %s\n", sqlite3_errstr(s));
+                sqlite3_finalize(stmt);
+                return NULL;
+            }
+        }
+    }
+
+    sqlite3_finalize(stmt);
+    result[row] = 0;
+
+    return result;
+}
+
+char*
+database_helper_get_string(sqlite3_stmt *stmt)
+{
+    char *result = NULL;
+
+    for(;;)
+    {
+        int s;
+
+        s = sqlite3_step(stmt);
+        if (s == SQLITE_ROW)
+        {
+            const char *found_string = (const char *)sqlite3_column_text(stmt, 0);
+            result = (char*)malloc(sizeof(char) * (strlen(found_string) + 1));
+            strcpy(result, found_string);
+        }
+        else
+        {
+            if (s == SQLITE_DONE)
+            {
+                break;
+            }
+            else
+            {
+                LOGGER_ERR("error selecting string from database: %s\n", sqlite3_errstr(s));
+                sqlite3_finalize(stmt);
+                return NULL;
+            }
+        }
+    }
+
+    sqlite3_finalize(stmt);
+
+    return result;
+}
+
+char**
+database_helper_get_strings(sqlite3_stmt *stmt)
+{
+    char **result = malloc(sizeof(char*));
+
+    int row = 0;
+
+    for(;;)
+    {
+        int s;
+
+        s = sqlite3_step(stmt);
+        if (s == SQLITE_ROW)
+        {
+            const char *new_string = (const char *)sqlite3_column_text(stmt, 0);
+            int new_string_len = strlen(new_string);
+            row++;
+
+            result = (char**)realloc(result, sizeof(char*) * (row + 1));
+            result[row - 1] = malloc(sizeof(char) * (new_string_len + 1));
+            strcpy(result[row - 1], new_string);
+        }
+        else
+        {
+            if(s == SQLITE_DONE)
+            {
+                break;
+            }
+            else
+            {
+                LOGGER_ERR("error selecting strings from database: %s\n", sqlite3_errstr(s));
+                break;
+            }
+        }
+    }
+    sqlite3_finalize(stmt);
+    result[row] = NULL;
+    return result;
+}
diff --git a/src/models/junction_tag.c b/src/models/junction_tag.c
index 21b40ae..17b198a 100644
--- a/src/models/junction_tag.c
+++ b/src/models/junction_tag.c
@@ -56,51 +56,6 @@ junction_tag_insert(int tag_id, int relay_id, int schedule_id)
     return 0;
 }
 
-static int*
-get_ids(sqlite3_stmt *stmt)
-{
-    int *ids = malloc(sizeof(int));
-    int new_id;
-
-    int row = 0;
-
-    while(true)
-    {
-        int s;
-
-        s = sqlite3_step(stmt);
-        if (s == SQLITE_ROW)
-        {
-            new_id = sqlite3_column_int(stmt, 0);
-            if(new_id != 0) // found row for other target (relay <> schedule)
-            {
-                row++;
-
-                ids = (int*)realloc(ids, sizeof(int) * (row + 1));
-                ids[row - 1] = new_id;
-            }
-        }
-        else
-        {
-            if (s == SQLITE_DONE)
-            {
-                break;
-            }
-            else
-            {
-                LOGGER_ERR("error selecting relays from database: %s\n", sqlite3_errstr(s));
-                sqlite3_finalize(stmt);
-                return NULL;
-            }
-        }
-    }
-
-    sqlite3_finalize(stmt);
-    ids[row] = 0;
-
-    return ids;
-}
-
 int*
 junction_tag_get_relays_for_tag_id(int tag_id)
 {
@@ -109,7 +64,7 @@ junction_tag_get_relays_for_tag_id(int tag_id)
     sqlite3_prepare_v2(global_database, "SELECT relay_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, NULL);
     sqlite3_bind_int(stmt, 1, tag_id);
 
-    return get_ids(stmt);
+    return database_helper_get_ids(stmt);
 }
 
 int*
@@ -120,7 +75,7 @@ junction_tag_get_schedules_for_tag_id(int tag_id)
     sqlite3_prepare_v2(global_database, "SELECT schedule_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, NULL);
     sqlite3_bind_int(stmt, 1, tag_id);
 
-    return get_ids(stmt);
+    return database_helper_get_ids(stmt);
 }
 
 int*
@@ -131,7 +86,7 @@ junction_tag_get_tags_for_relay_id(int relay_id)
     sqlite3_prepare_v2(global_database, "SELECT tag_id FROM junction_tag WHERE relay_id=?1;", -1, &stmt, NULL);
     sqlite3_bind_int(stmt, 1, relay_id);
 
-    return get_ids(stmt);
+    return database_helper_get_ids(stmt);
 }
 
 int*
@@ -142,7 +97,7 @@ junction_tag_get_tags_for_schedule_id(int schedule_id)
     sqlite3_prepare_v2(global_database, "SELECT tag_id FROM junction_tag WHERE schedule_id=?1;", -1, &stmt, NULL);
     sqlite3_bind_int(stmt, 1, schedule_id);
 
-    return get_ids(stmt);
+    return database_helper_get_ids(stmt);
 }
 
 int
diff --git a/src/models/relay.c b/src/models/relay.c
index a5fe564..9130faa 100644
--- a/src/models/relay.c
+++ b/src/models/relay.c
@@ -430,3 +430,14 @@ relay_get_by_controller_id(int controller_id)
     return relay_db_select(stmt);
 
 }
+
+int
+relay_get_controller_id_for_relay(int relay_id)
+{
+    sqlite3_stmt *stmt;
+
+    sqlite3_prepare_v2(global_database, "SELECT controller_id FROM relays WHERE relay_id=?1;", -1, &stmt, NULL);
+    sqlite3_bind_int(stmt, 1, relay_id);
+
+    return database_helper_get_id(stmt);
+}
diff --git a/src/models/tag.c b/src/models/tag.c
index 63615f1..589298d 100644
--- a/src/models/tag.c
+++ b/src/models/tag.c
@@ -47,37 +47,7 @@ tag_get_tag(int id)
     sqlite3_prepare_v2(global_database, "SELECT tag FROM tags WHERE id=?1;", -1, &stmt, NULL);
     sqlite3_bind_int(stmt, 1, id);
 
-    char *result = NULL;
-
-    while(1)
-    {
-        int s;
-
-        s = sqlite3_step(stmt);
-        if (s == SQLITE_ROW)
-        {
-            const char *found_tag = (const char *)sqlite3_column_text(stmt, 0);
-            result = (char*)malloc(sizeof(char) * (strlen(found_tag) + 1));
-            strcpy(result, found_tag);
-        }
-        else
-        {
-            if (s == SQLITE_DONE)
-            {
-                break;
-            }
-            else
-            {
-                LOGGER_ERR("error selecting tags from database: %s\n", sqlite3_errstr(s));
-                sqlite3_finalize(stmt);
-                return NULL;
-            }
-        }
-    }
-
-    sqlite3_finalize(stmt);
-
-    return result;
+    return database_helper_get_string(stmt);
 }
 
 char**
@@ -87,41 +57,7 @@ tag_get_all()
 
     sqlite3_prepare_v2(global_database, "SELECT tag FROM tags;", -1, &stmt, NULL);
 
-    char **all_tags = malloc(sizeof(char*));
-
-    int row = 0;
-
-    while(true)
-    {
-        int s;
-
-        s = sqlite3_step(stmt);
-        if (s == SQLITE_ROW)
-        {
-            const char *new_tag = (const char *)sqlite3_column_text(stmt, 0);
-            int new_tag_len = strlen(new_tag);
-            row++;
-
-            all_tags = (char**)realloc(all_tags, sizeof(char*) * (row + 1));
-            all_tags[row - 1] = malloc(sizeof(char) * (new_tag_len + 1));
-            strcpy(all_tags[row - 1], new_tag);
-        }
-        else
-        {
-            if(s == SQLITE_DONE)
-            {
-                break;
-            }
-            else
-            {
-                LOGGER_ERR("error selecting tags from database: %s\n", sqlite3_errstr(s));
-                break;
-            }
-        }
-    }
-    sqlite3_finalize(stmt);
-    all_tags[row] = NULL;
-    return all_tags;
+    return database_helper_get_strings(stmt);
 }
 
 int
@@ -132,35 +68,7 @@ tag_get_id(const char *tag)
     sqlite3_prepare_v2(global_database, "SELECT id FROM tags WHERE tag=?1;", -1, &stmt, NULL);
     sqlite3_bind_text(stmt, 1, tag, -1, SQLITE_STATIC);
 
-    int result = 0;
-
-    while(1)
-    {
-        int s;
-
-        s = sqlite3_step(stmt);
-        if (s == SQLITE_ROW)
-        {
-            result = sqlite3_column_int(stmt, 0);
-        }
-        else
-        {
-            if (s == SQLITE_DONE)
-            {
-                break;
-            }
-            else
-            {
-                LOGGER_ERR("error selecting tags from database: %s\n", sqlite3_errstr(s));
-                sqlite3_finalize(stmt);
-                return 0;
-            }
-        }
-    }
-
-    sqlite3_finalize(stmt);
-
-    return result;
+    return database_helper_get_id(stmt);
 }
 
 int