profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/TheBestPessimist/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
TheBestPessimist TheBestPessimist ~/Downloads tbp.land

TheBestPessimist/duplicacy-utils 46

Run Duplicacy on schedule on Windows and Linux

TheBestPessimist/AutoHotKey-Scripts 8

From starting apps to locking computer to autoclicking in games. Autohotkey does it all!

TheBestPessimist/Duplicacy-Utils-Telegram-Bot 1

A telegram bot used by my duplicacy-utils scripts

TheBestPessimist/Duplicacy-Utils-Telegram-Bot-Kt 1

A telegram bot used by my duplicacy-utils scripts to notify backup status. Host it yourself!

enescualexandru/url-shortener 0

Sample project for learning Kotlin

TheBestPessimist/bl3hotfixmodding 0

Borderlands 3 Hotfix Modding

Pull request review commentenescualexandru/url-shortener

Add caching

 private val baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build class UrlServiceImpl(     private val urlEntryRepository: UrlEntryRepository,

why do we use both UrlEntryRepository and UrlEntryRepositoryBase?

UrlEntryRepository is a child of UrlEntryRepositoryBase, so this looks awkward

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener.data.cache++import com.shortener.data.domain.EncodedSequence+import com.shortener.data.domain.UrlEntry+import com.shortener.data.repository.UrlEntryRepositoryBase+import org.springframework.beans.factory.annotation.Autowired+import org.springframework.beans.factory.annotation.Qualifier+import org.springframework.cache.annotation.CacheEvict+import org.springframework.cache.annotation.Cacheable+import org.springframework.stereotype.Component++const val CACHE_NAME_URL_ENTRY = "url-entry-cache"++@Component+class UrlEntryRepositoryCacheImpl : UrlEntryRepositoryBase {

Architectural q

  1. Why do we cache spring entities? afaiu caches should hold *actually useful* data, and an Entity is anything but that. It is an oversized monstrosity (contains so much JPA/Spring AOP bloat it scares me) from the POV of a cache.
  2. (i never worked with redis + spring, so bear with me) Why is the cache another Repository? Is this how Spring best practices suggests we implement caching? Is this how caching is supposed to work? I would like to hear your thoughts here.
  3. If this is a Repository, why is it annotated with @Component?
  4. Are we supposed to never use UrlEntryRepository again, since we will potentially be missing on caching?
enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

 private val baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build class UrlServiceImpl(     private val urlEntryRepository: UrlEntryRepository,     private val idEncoder: IdEncoder,-    private val urlValidator: UrlValidator+    private val urlValidator: UrlValidator,+    private val urlEntryRepositoryCacheImpl: UrlEntryRepositoryBase

architectural question: why does UrlServiceImpl care if the repository uses cache or not? It is reflected in the name of the variable.

What bothers me more though, is that now we ahve 2 repositories: one with cache, another without. during autowiring someone might ask for the wrong repository.

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener.data.cache++import com.shortener.TestUtils+import com.shortener.data.domain.EncodedSequence+import com.shortener.data.domain.UrlEntry+import com.shortener.data.repository.UrlEntryRepositoryBase+import org.assertj.core.api.Assertions+import org.junit.jupiter.api.BeforeEach+import org.junit.jupiter.api.Test+import org.springframework.beans.factory.annotation.Autowired+import org.springframework.boot.test.context.SpringBootTest+import org.springframework.cache.CacheManager+import org.springframework.data.redis.core.RedisTemplate+import org.springframework.test.context.ActiveProfiles+import org.springframework.transaction.annotation.Transactional++@ActiveProfiles("test-cache")+@SpringBootTest(classes = [TestCacheConfig::class])+@Transactional+class UrlEntryRepositoryCacheImplTests(+    @Autowired val urlEntryRepositoryCacheImpl: UrlEntryRepositoryBase,+    @Autowired val redisTemplate: RedisTemplate<String, UrlEntry>,+    @Autowired val cacheManager: CacheManager+) {++    @BeforeEach+    fun clearCache() {+        cacheManager.cacheNames.forEach { cacheManager.getCache(it)!!.clear() }+    }++    @Test+    fun `Entries not cached upon saving`() {+        val urlEntry = TestUtils.createUrlEntry()+        urlEntryRepositoryCacheImpl.save(urlEntry)+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(0)+    }++    @Test+    fun `Non existent entries are not cached`() {+        urlEntryRepositoryCacheImpl.findByEncodedSequence(EncodedSequence().apply { sequence = "a1b1" })+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(0)+    }++    @Test+    fun `Bad arguments, no entry will be cached`() {+        urlEntryRepositoryCacheImpl.findByEncodedSequence(EncodedSequence().apply { sequence = "" })+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(0)+    }++    @Test+    fun `Entries are cached on retrieval`() {+        val urlEntry = TestUtils.createUrlEntry()+        val persistedEntry = urlEntryRepositoryCacheImpl.save(urlEntry)+        val encodedSequence = persistedEntry.encodedSequence!!.sequence+        urlEntryRepositoryCacheImpl.findByEncodedSequence(EncodedSequence().apply { sequence = encodedSequence })+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(1)+        Assertions.assertThat(isSequenceCached(encodedSequence)).isTrue+        Assertions.assertThat(persistedEntry).isEqualTo(getUrlEntryFromCache(encodedSequence))+    }++    @Test+    fun `Entries are evicted from cache on deletion`() {+        val urlEntry = TestUtils.createUrlEntry()+        var persistedEntry = urlEntryRepositoryCacheImpl.save(urlEntry)+        val encodedSequence = persistedEntry.encodedSequence!!.sequence+        persistedEntry =+            urlEntryRepositoryCacheImpl.findByEncodedSequence(EncodedSequence().apply { sequence = encodedSequence })!!+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(1)++        urlEntryRepositoryCacheImpl.delete(persistedEntry)+        Assertions.assertThat(getAllKeysFromCache().size).isEqualTo(0)+    }++    fun getAllKeysFromCache(): Set<ByteArray> =+        redisTemplate.connectionFactory!!.connection.keys("*".toByteArray()) as Set<ByteArray>++    fun isSequenceCached(sequence: String): Boolean =+        getAllKeysFromCache().map { getKeyFromByteArray(it) }.any { it.contains(sequence) }++    fun getKeyFromByteArray(key: ByteArray): String = redisTemplate.keySerializer.deserialize(key).toString()++    fun getValueFromByteArray(value: ByteArray): UrlEntry? =+        redisTemplate.valueSerializer.deserialize(value) as UrlEntry++    fun getUrlEntryFromCache(sequence: String?): UrlEntry? =

can these helper functions be private?

enescualexandru

comment created time in 10 days

PullRequestReviewEvent

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener++import com.shortener.data.domain.EncodedSequence+import com.shortener.data.domain.UrlEntry+import java.time.LocalDateTime++const val GOOD_URL = "http://www.google.com"+const val GOOD_URL_NO_SCHEMA = "google.com"+const val BAD_URL = "http://$#%oole.com"+const val BAD_URL_NO_SCHEMA = "$#%oole.com"+const val DEFAULT_SEQUENCE = "a1b2"+const val DEFAULT_EXPIRED = false++class TestUtils {

This class acts as a singleton containing (probably) only static functions. In this case, you can make TestUtils an object (another concept to read about).

By having object TestUtils, you no longer need a companion object. Each of the functions inside are static.

You may also consider removing TestUtils completely as it has a single function and leave it as a top level function, similar to the const vals above (top level variables).

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.datasource.driver-class-name=org.postgresql.Driver++spring.cache.redis.cache-null-values=false+# 24 hours+spring.cache.redis.time-to-live=1440000

if we dont cache by default, how do we use redis?

isnt

spring.cache.type=redis

missing?

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener.data.cache++import com.shortener.data.domain.EncodedSequence+import com.shortener.data.domain.UrlEntry+import com.shortener.data.repository.UrlEntryRepositoryBase+import org.springframework.beans.factory.annotation.Autowired+import org.springframework.beans.factory.annotation.Qualifier+import org.springframework.cache.annotation.CacheEvict+import org.springframework.cache.annotation.Cacheable+import org.springframework.stereotype.Component++const val CACHE_NAME_URL_ENTRY = "url-entry-cache"++@Component+class UrlEntryRepositoryCacheImpl : UrlEntryRepositoryBase {++    @Autowired+    @Qualifier("urlEntryRepository")+    private lateinit var urlEntryRepository: UrlEntryRepositoryBase

Please dont use @Autowired on fields in main. I can accept that in test, but not in main.

Instead use constructor parameters.

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener++import com.shortener.data.domain.EncodedSequence+import com.shortener.data.domain.UrlEntry+import java.time.LocalDateTime++const val GOOD_URL = "http://www.google.com"+const val GOOD_URL_NO_SCHEMA = "google.com"+const val BAD_URL = "http://$#%oole.com"+const val BAD_URL_NO_SCHEMA = "$#%oole.com"+const val DEFAULT_SEQUENCE = "a1b2"+const val DEFAULT_EXPIRED = false++class TestUtils {++    companion object {+        fun createUrlEntry(+            longUrl: String = GOOD_URL,+            encodedSequenceStr: String = DEFAULT_SEQUENCE,+            expired: Boolean = DEFAULT_EXPIRED+        ): UrlEntry =+            UrlEntry(longUrl).apply {+                encodedSequence = EncodedSequence().apply { sequence = encodedSequenceStr }

I have seen this pattern EncodedSequence().apply { sequence = in code a lot of times and it started to bother me. Maybe you can add sequence as the first constructor param (or in a secondary constructor) instead.

enescualexandru

comment created time in 10 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentenescualexandru/url-shortener

Add caching

+package com.shortener.data.cache++import com.shortener.data.domain.UrlEntry+import org.springframework.cache.annotation.EnableCaching+import org.springframework.context.annotation.Bean+import org.springframework.context.annotation.Configuration+import org.springframework.data.redis.connection.RedisConnectionFactory+import org.springframework.data.redis.core.RedisTemplate+import org.springframework.data.redis.serializer.StringRedisSerializer++@EnableCaching+@Configuration+class RedisCacheConfiguration {++    @Bean+    fun redisTemplateUrlEntry(connectionFactory: RedisConnectionFactory?): RedisTemplate<String, UrlEntry> {

why do we want to receive a nullable connectionFactory ?

enescualexandru

comment created time in 10 days

Pull request review commentenescualexandru/url-shortener

Add caching

 services:       - POSTGRES_PASSWORD=postgres       - POSTGRES_DB=url-shortener +  redis:

dont forget about depends on redis

enescualexandru

comment created time in 10 days

PullRequestReviewEvent

Pull request review commentenescualexandru/url-shortener

Add caching

 services:       - POSTGRES_PASSWORD=postgres       - POSTGRES_DB=url-shortener +  redis:+    image: "redis:alpine"+    container_name: redis+    command: redis-server --requirepass redis+    ports:+      - "6379:6379"+    volumes:+      - redis-data:/var/lib/redis+      - redis-conf:/usr/local/etc/redis/redis.conf+    environment:+      - REDIS_REPLICATION_MODE=master+ volumes:-  postgres-data:\ No newline at end of file+  postgres-data:+  redis-data:+  redis-conf:

no newline at the end of file image

enescualexandru

comment created time in 10 days

PullRequestReviewEvent

Pull request review commentenescualexandru/url-shortener

Add caching

 Meaning that for eg. `google.com` will be accepted, while `$#@le.com` will not. - `docker-compose up --build` - open any browser and go to `localhost` or `localhost:8080` -**NOTE:** two containers are started, one being the postgresql db and the other one, our spring boot app. The app's jvm+**NOTE:** Three containers are started: redis, postgresql db & the spring boot app. The app's jvm is started on debug mode, the debug port is `5005`. The postgres db can be reached at port `5432`, with the dbname, user and password specified in the docker-compose file.++**NOTE FOR CACHING:**  Redis cli can be accessed by `docker exec -it redis redis-cli`, then `auth {password}`(password in the docker-compose file).

super very nice documentation!

enescualexandru

comment created time in 10 days

PullRequestReviewEvent

delete branch enescualexandru/url-shortener

delete branch : project_setup

delete time in 11 days

push eventenescualexandru/url-shortener

enescualexandru

commit sha 08fb4d0cdf21255b10c3437640d3f9b79515da74

Added project structure and basic implementation (#1) * Added project structure and basic implementation * Addressed code review comments * Replace Optional with nullable type * Style * Use non-null as much as possible in entities * Made the main function as one liner * Ensured the service method 'shortenUrl' is transactional; corrected the import in the tests * Added explicit column length, where required * Improvements - amended the readme file, with some clarifications - corrected the constructor for hashids, enabling a minimum hash length of 4 - changed the docker file, added eclipse temurin as the base image for the app, adoptopenjdk being as of now, deprecated * Changed the dto classes to data classes Co-authored-by: TheBestPessimist <cristian@tbp.land>

view details

push time in 11 days

push eventTheBestPessimist/gradle-buildSrc-example

TheBestPessimist

commit sha 8177332319743c9e3c0e49c29815666167977585

Question about how to use a common versions object

view details

push time in 18 days

push eventTheBestPessimist/gradle-buildSrc-example

TheBestPessimist

commit sha bd76d26029317a11e950dc248b1deb1dd68aea12

Add readme

view details

TheBestPessimist

commit sha aa35e6a2b7e69ce79777b5beb5340fda01b24362

Add multiple plugins and dependencies

view details

push time in 19 days

create barnchTheBestPessimist/gradle-buildSrc-example

branch : master

created branch time in 19 days

created repositoryTheBestPessimist/gradle-buildSrc-example

created time in 19 days

Pull request review commentenescualexandru/url-shortener

Added project structure and basic implementation

+package com.shortener++import org.springframework.boot.autoconfigure.SpringBootApplication+import org.springframework.boot.runApplication++@SpringBootApplication+class ShortenerApplication++fun main(args: Array<String>) {

I think that's a good practice anyway (defining the main manually), as what should happen if there are multiple main functions? In a project there can be multiple mains for any number of reasons

enescualexandru

comment created time in 20 days

PullRequestReviewEvent

push eventenescualexandru/url-shortener

TheBestPessimist

commit sha c2dc8f5299e515965b124716beb7baed3b6916ad

Replace Optional with nullable type

view details

TheBestPessimist

commit sha 9de62aec63d1d818ed61cfd75f74119717ffac33

Style

view details

TheBestPessimist

commit sha b2404f28ac9dfc767acdfe7ecc32a81589a54874

Use non-null as much as possible in entities

view details

push time in 20 days

Pull request review commentenescualexandru/url-shortener

Added project structure and basic implementation

+package com.shortener.dto++class UrlShortenResponse(var shortenedUrl: String)

Same as above: the class should be a data class

enescualexandru

comment created time in 20 days

PullRequestReviewEvent