CSHARP_1001_NOTES Telegram 680
πŸ“Œ Π—Π°Π΄Π°Ρ‡Π°: "ВысоконагруТённый кэш с автоматичСской очисткой ΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹ΠΌ доступом"

❗️УсловиС:

Π Π΅Π°Π»ΠΈΠ·ΡƒΠΉΡ‚Π΅ класс SmartCache<TKey, TValue> Π² .NET, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π΄ΠΎΠ»ΠΆΠ΅Π½:

- ΠŸΠΎΠ·Π²ΠΎΠ»ΡΡ‚ΡŒ бСзопасно Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ элСмСнты ΠΈΠ· кэша Π² ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ срСдС (`Get`, `Set`).
- АвтоматичСски ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ элСмСнты Ρ‡Π΅Ρ€Π΅Π· N сСкунд послС ΠΈΡ… добавлСния (TTL).
- ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ Π²Ρ‹ΡΠΎΠΊΡƒΡŽ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΈ массовом доступС (тысячи ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ Π² сСкунду).
- ΠœΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ (`lock`) ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‰ΠΈΠ΅ структуры.
- ΠšΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΈΡΡ‚Π΅ΠΊΡˆΠΈΠΌΠΈ элСмСнтами:
- НС Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ ΠΈΡ… Ρ‡Π΅Ρ€Π΅Π· Get.
- НС ΠΊΠΎΠΏΠΈΡ‚ΡŒ мусор Π² памяти.

---

β–ͺ️ ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΡ:

- МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ стандартныС ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ .NET (`ConcurrentDictionary`, Timer, Task, CancellationToken ΠΈ Ρ‚.Π΄.).
- НСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ внСшниС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Ρ‚ΠΈΠΏΠ° MemoryCache, Redis, LazyCache ΠΈ Π΄Ρ€.
- НуТно ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΠΎΠ΄ большой Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ (ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π΅ΠΉ ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ).

---

β–ͺ️ Подсказки:

- Для ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½ΠΎΠ³ΠΎ доступа ΠΏΠΎΠ΄ΠΎΠΉΠ΄Ρ‘Ρ‚ ConcurrentDictionary<TKey, ValueWithExpiry>.
- Для очистки ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΡ… Π΄Π°Π½Π½Ρ‹Ρ…:
- МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ„ΠΎΠ½ΠΎΠ²ΡƒΡŽ Π·Π°Π΄Π°Ρ‡Ρƒ (`Task`) с Ρ‚Π°ΠΉΠΌΠ΅Ρ€ΠΎΠΌ, которая пСриодичСски чистит старыС записи.
- ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° Π³ΠΎΠ½ΠΊΠΈ состояний: ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ срока ΠΆΠΈΠ·Π½ΠΈ элСмСнта ΠΈ Π΅Π³ΠΎ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ΠΌ.

---

β–ͺ️ Π§Ρ‚ΠΎ оцСниваСтся:

- Π£ΠΌΠ΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ потокобСзопасныС структуры Π΄Π°Π½Π½Ρ‹Ρ….
- ΠŸΡ€ΠΎΠ΄ΡƒΠΌΠ°Π½Π½ΠΎΡΡ‚ΡŒ балансировки ΠΌΠ΅ΠΆΠ΄Ρƒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒΡŽ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΈ частотой очистки.
- ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π°Ρ Ρ€Π°Π±ΠΎΡ‚Π° со Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ (`TTL`).
- Чистота ΠΈ Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π°.

---

β–ͺ️ Π Π°Π·Π±ΠΎΡ€ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠ³ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ:

β–ͺ️ Основная идСя:

- Π’ кэшС Ρ…Ρ€Π°Π½ΠΈΠΌ Π½Π΅ просто Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Π° ΠΏΠ°Ρ€Ρƒ (Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ + врСмя истСчСния).
- ΠŸΡ€ΠΈ Get(key):
- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, истёк Π»ΠΈ элСмСнт.
- Если истёк β€” удаляСм Π΅Π³ΠΎ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ null ΠΈΠ»ΠΈ default.
- ΠŸΡ€ΠΈ Set(key, value):
- БохраняСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ с Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ + TTL.
- ΠžΡ‚Π΄Π΅Π»ΡŒΠ½Π°Ρ фоновая Π·Π°Π΄Π°Ρ‡Π° (`Task`) рСгулярно сканируСт кэш ΠΈ удаляСт ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠ΅ элСмСнты.

β–ͺ️ Мини-ΠΏΡ€ΠΈΠΌΠ΅Ρ€ структуры:


using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class SmartCache<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, (TValue Value, DateTime Expiry)> _cache = new();
private readonly TimeSpan _ttl;
private readonly CancellationTokenSource _cts = new();

public SmartCache(TimeSpan ttl)
{
_ttl = ttl;
StartCleanupTask();
}

public void Set(TKey key, TValue value)
{
_cache[key] = (value, DateTime.UtcNow.Add(_ttl));
}

public TValue Get(TKey key)
{
if (_cache.TryGetValue(key, out var entry))
{
if (entry.Expiry > DateTime.UtcNow)
{
return entry.Value;
}
else
{
_cache.TryRemove(key, out _);
}
}
return default;
}

private void StartCleanupTask()
{
Task.Run(async () =>
{
while (!_cts.Token.IsCancellationRequested)
{
foreach (var key in _cache.Keys)
{
if (_cache.TryGetValue(key, out var entry) && entry.Expiry <= DateTime.UtcNow)
{
_cache.TryRemove(key, out _);
}
}
await Task.Delay(TimeSpan.FromSeconds(30), _cts.Token); // пСриодичСская очистка
}
});
}

public void Dispose()
{
_cts.Cancel();
}
}

πŸ“Œ Π’Π°ΠΆΠ½Ρ‹Π΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹:

- Кэш ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹ΠΉ (`ConcurrentDictionary`) β€” доступ Π±Π΅Π· явных Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ.
- ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π΅ΡΠΊΠ°Ρ чистка Π½Π΅ ΠΌΠ΅ΡˆΠ°Π΅Ρ‚ основным опСрациям.
- Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΈΡΡ‚Ρ‘ΠΊΡˆΠΈΡ… элСмСнтов происходит "мягко" (Ρ‡Π΅Ρ€Π΅Π· ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ срока ΠΆΠΈΠ·Π½ΠΈ).
- Ѐоновая Π·Π°Π΄Π°Ρ‡Π° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· CancellationToken.



tgoop.com/csharp_1001_notes/680
Create:
Last Update:

πŸ“Œ Π—Π°Π΄Π°Ρ‡Π°: "ВысоконагруТённый кэш с автоматичСской очисткой ΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹ΠΌ доступом"

❗️УсловиС:

Π Π΅Π°Π»ΠΈΠ·ΡƒΠΉΡ‚Π΅ класс SmartCache<TKey, TValue> Π² .NET, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π΄ΠΎΠ»ΠΆΠ΅Π½:

- ΠŸΠΎΠ·Π²ΠΎΠ»ΡΡ‚ΡŒ бСзопасно Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ элСмСнты ΠΈΠ· кэша Π² ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ срСдС (`Get`, `Set`).
- АвтоматичСски ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ элСмСнты Ρ‡Π΅Ρ€Π΅Π· N сСкунд послС ΠΈΡ… добавлСния (TTL).
- ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ Π²Ρ‹ΡΠΎΠΊΡƒΡŽ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΈ массовом доступС (тысячи ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ Π² сСкунду).
- ΠœΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ (`lock`) ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‰ΠΈΠ΅ структуры.
- ΠšΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΈΡΡ‚Π΅ΠΊΡˆΠΈΠΌΠΈ элСмСнтами:
- НС Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ ΠΈΡ… Ρ‡Π΅Ρ€Π΅Π· Get.
- НС ΠΊΠΎΠΏΠΈΡ‚ΡŒ мусор Π² памяти.

---

β–ͺ️ ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΡ:

- МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ стандартныС ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ .NET (`ConcurrentDictionary`, Timer, Task, CancellationToken ΠΈ Ρ‚.Π΄.).
- НСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ внСшниС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Ρ‚ΠΈΠΏΠ° MemoryCache, Redis, LazyCache ΠΈ Π΄Ρ€.
- НуТно ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΠΎΠ΄ большой Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ (ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π΅ΠΉ ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ).

---

β–ͺ️ Подсказки:

- Для ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½ΠΎΠ³ΠΎ доступа ΠΏΠΎΠ΄ΠΎΠΉΠ΄Ρ‘Ρ‚ ConcurrentDictionary<TKey, ValueWithExpiry>.
- Для очистки ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΡ… Π΄Π°Π½Π½Ρ‹Ρ…:
- МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ„ΠΎΠ½ΠΎΠ²ΡƒΡŽ Π·Π°Π΄Π°Ρ‡Ρƒ (`Task`) с Ρ‚Π°ΠΉΠΌΠ΅Ρ€ΠΎΠΌ, которая пСриодичСски чистит старыС записи.
- ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° Π³ΠΎΠ½ΠΊΠΈ состояний: ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ срока ΠΆΠΈΠ·Π½ΠΈ элСмСнта ΠΈ Π΅Π³ΠΎ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ΠΌ.

---

β–ͺ️ Π§Ρ‚ΠΎ оцСниваСтся:

- Π£ΠΌΠ΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ потокобСзопасныС структуры Π΄Π°Π½Π½Ρ‹Ρ….
- ΠŸΡ€ΠΎΠ΄ΡƒΠΌΠ°Π½Π½ΠΎΡΡ‚ΡŒ балансировки ΠΌΠ΅ΠΆΠ΄Ρƒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒΡŽ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ ΠΈ частотой очистки.
- ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π°Ρ Ρ€Π°Π±ΠΎΡ‚Π° со Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ (`TTL`).
- Чистота ΠΈ Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π°.

---

β–ͺ️ Π Π°Π·Π±ΠΎΡ€ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠ³ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ:

β–ͺ️ Основная идСя:

- Π’ кэшС Ρ…Ρ€Π°Π½ΠΈΠΌ Π½Π΅ просто Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Π° ΠΏΠ°Ρ€Ρƒ (Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ + врСмя истСчСния).
- ΠŸΡ€ΠΈ Get(key):
- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, истёк Π»ΠΈ элСмСнт.
- Если истёк β€” удаляСм Π΅Π³ΠΎ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ null ΠΈΠ»ΠΈ default.
- ΠŸΡ€ΠΈ Set(key, value):
- БохраняСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ с Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ + TTL.
- ΠžΡ‚Π΄Π΅Π»ΡŒΠ½Π°Ρ фоновая Π·Π°Π΄Π°Ρ‡Π° (`Task`) рСгулярно сканируСт кэш ΠΈ удаляСт ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠ΅ элСмСнты.

β–ͺ️ Мини-ΠΏΡ€ΠΈΠΌΠ΅Ρ€ структуры:


using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class SmartCache<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, (TValue Value, DateTime Expiry)> _cache = new();
private readonly TimeSpan _ttl;
private readonly CancellationTokenSource _cts = new();

public SmartCache(TimeSpan ttl)
{
_ttl = ttl;
StartCleanupTask();
}

public void Set(TKey key, TValue value)
{
_cache[key] = (value, DateTime.UtcNow.Add(_ttl));
}

public TValue Get(TKey key)
{
if (_cache.TryGetValue(key, out var entry))
{
if (entry.Expiry > DateTime.UtcNow)
{
return entry.Value;
}
else
{
_cache.TryRemove(key, out _);
}
}
return default;
}

private void StartCleanupTask()
{
Task.Run(async () =>
{
while (!_cts.Token.IsCancellationRequested)
{
foreach (var key in _cache.Keys)
{
if (_cache.TryGetValue(key, out var entry) && entry.Expiry <= DateTime.UtcNow)
{
_cache.TryRemove(key, out _);
}
}
await Task.Delay(TimeSpan.FromSeconds(30), _cts.Token); // пСриодичСская очистка
}
});
}

public void Dispose()
{
_cts.Cancel();
}
}

πŸ“Œ Π’Π°ΠΆΠ½Ρ‹Π΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹:

- Кэш ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Ρ‹ΠΉ (`ConcurrentDictionary`) β€” доступ Π±Π΅Π· явных Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ.
- ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π΅ΡΠΊΠ°Ρ чистка Π½Π΅ ΠΌΠ΅ΡˆΠ°Π΅Ρ‚ основным опСрациям.
- Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΈΡΡ‚Ρ‘ΠΊΡˆΠΈΡ… элСмСнтов происходит "мягко" (Ρ‡Π΅Ρ€Π΅Π· ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ срока ΠΆΠΈΠ·Π½ΠΈ).
- Ѐоновая Π·Π°Π΄Π°Ρ‡Π° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· CancellationToken.

BY C# 1001 notes


Share with your friend now:
tgoop.com/csharp_1001_notes/680

View MORE
Open in Telegram


Telegram News

Date: |

More>> Content is editable within two days of publishing With the sharp downturn in the crypto market, yelling has become a coping mechanism for many crypto traders. This screaming therapy became popular after the surge of Goblintown Ethereum NFTs at the end of May or early June. Here, holders made incoherent groaning sounds in late-night Twitter spaces. They also role-played as urine-loving Goblin creatures. Invite up to 200 users from your contacts to join your channel Done! Now you’re the proud owner of a Telegram channel. The next step is to set up and customize your channel.
from us


Telegram C# 1001 notes
FROM American